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

from pytest_pyodide import run_in_pyodide

THIS = """
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
"""


def test_wordcloud(selenium):
    @run_in_pyodide(packages=["wordcloud"])
    def _test_wordcloud_inner(selenium, words):
        from wordcloud.wordcloud import WordCloud

        wc = WordCloud()
        wc.generate(words)

        assert wc.words_ == {
            "better": 1.0,
            "Although": 0.375,
            "never": 0.375,
            "one": 0.375,
            "idea": 0.375,
            "complex": 0.25,
            "Special": 0.25,
            "Unless": 0.25,
            "obvious": 0.25,
            "way": 0.25,
            "may": 0.25,
            "Now": 0.25,
            "implementation": 0.25,
            "explain": 0.25,
            "Beautiful": 0.125,
            "ugly": 0.125,
            "Explicit": 0.125,
            "implicit": 0.125,
            "Simple": 0.125,
            "complicated": 0.125,
            "Flat": 0.125,
            "nested": 0.125,
            "Sparse": 0.125,
            "dense": 0.125,
            "Readability": 0.125,
            "counts": 0.125,
            "cases": 0.125,
            "enough": 0.125,
            "break": 0.125,
            "rules": 0.125,
            "practicality": 0.125,
            "beats": 0.125,
            "purity": 0.125,
            "Errors": 0.125,
            "pass": 0.125,
            "silently": 0.125,
            "explicitly": 0.125,
            "silenced": 0.125,
            "face": 0.125,
            "ambiguity": 0.125,
            "refuse": 0.125,
            "temptation": 0.125,
            "guess": 0.125,
            "preferably": 0.125,
            "first": 0.125,
            "Dutch": 0.125,
            "often": 0.125,
            "right": 0.125,
            "hard": 0.125,
            "bad": 0.125,
            "easy": 0.125,
            "good": 0.125,
            "Namespaces": 0.125,
            "honking": 0.125,
            "great": 0.125,
            "let": 0.125,
        }

        svg = wc.to_svg()
        assert svg.startswith("<svg") and svg.endswith("</svg>")

    _test_wordcloud_inner(selenium, THIS)

from pytest_pyodide import run_in_pyodide

THIS = """
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
"""


def test_wordcloud(selenium):
    @run_in_pyodide(packages=["wordcloud"])
    def _test_wordcloud_inner(selenium, words):
        from wordcloud.wordcloud import WordCloud

        wc = WordCloud()
        wc.generate(words)

        assert wc.words_ == {
            "better": 1.0,
            "Although": 0.375,
            "never": 0.375,
            "one": 0.375,
            "idea": 0.375,
            "complex": 0.25,
            "Special": 0.25,
            "Unless": 0.25,
            "obvious": 0.25,
            "way": 0.25,
            "may": 0.25,
            "Now": 0.25,
            "implementation": 0.25,
            "explain": 0.25,
            "Beautiful": 0.125,
            "ugly": 0.125,
            "Explicit": 0.125,
            "implicit": 0.125,
            "Simple": 0.125,
            "complicated": 0.125,
            "Flat": 0.125,
            "nested": 0.125,
            "Sparse": 0.125,
            "dense": 0.125,
            "Readability": 0.125,
            "counts": 0.125,
            "cases": 0.125,
            "enough": 0.125,
            "break": 0.125,
            "rules": 0.125,
            "practicality": 0.125,
            "beats": 0.125,
            "purity": 0.125,
            "Errors": 0.125,
            "pass": 0.125,
            "silently": 0.125,
            "explicitly": 0.125,
            "silenced": 0.125,
            "face": 0.125,
            "ambiguity": 0.125,
            "refuse": 0.125,
            "temptation": 0.125,
            "guess": 0.125,
            "preferably": 0.125,
            "first": 0.125,
            "Dutch": 0.125,
            "often": 0.125,
            "right": 0.125,
            "hard": 0.125,
            "bad": 0.125,
            "easy": 0.125,
            "good": 0.125,
            "Namespaces": 0.125,
            "honking": 0.125,
            "great": 0.125,
            "let": 0.125,
        }

        svg = wc.to_svg()
        assert svg.startswith("<svg") and svg.endswith("</svg>")

    _test_wordcloud_inner(selenium, THIS)
