We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Python 3.12 version
## 🚀 Feature

<!-- A clear and concise description of the feature proposal -->

Hi, I tried [REPL](https://pyodide.org/en/stable/console.html), maybe it uses the latest 0.25.0, and I noticed that the python is 3.11.3.

Python 3.12 has released for a few months with a lot of new features. Since there is no issue track the progress. So, I created this one.

### Motivation

<!-- Please outline the motivation for the proposal. Is your feature request related to a problem? e.g., I'm always frustrated when [...]. If this is related to another GitHub issue, please link here too -->

N.A.

### Pitch

<!-- A clear and concise description of what you want to happen. -->

N.A.

### Alternatives

<!-- A clear and concise description of any alternative solutions or features you've considered, if any. -->

N.A.

### Additional context

<!-- Add any other context or screenshots about the feature request here. -->

N.A.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pyodide-build/pyodide_build/pyzip.py`
Content:
```
1 import shutil
2 from collections.abc import Callable
3 from pathlib import Path
4 from tempfile import TemporaryDirectory
5 
6 from ._py_compile import _compile
7 from .common import make_zip_archive
8 
9 # These files are removed from the stdlib
10 REMOVED_FILES = (
11     # package management
12     "ensurepip/",
13     "venv/",
14     # build system
15     "lib2to3/",
16     # other platforms
17     "_osx_support.py",
18     "_aix_support.py",
19     # Not supported by browser
20     "curses/",
21     "dbm/",
22     "idlelib/",
23     "tkinter/",
24     "turtle.py",
25     "turtledemo",
26 )
27 
28 # These files are unvendored from the stdlib and can be loaded with `loadPackage`
29 UNVENDORED_FILES = (
30     "test/",
31     "distutils/",
32     "sqlite3",
33     "ssl.py",
34     "lzma.py",
35     "_pydecimal.py",
36     "pydoc_data",
37 )
38 
39 # We have JS implementations of these modules
40 JS_STUB_FILES = ("webbrowser.py",)
41 
42 
43 def default_filterfunc(
44     root: Path, verbose: bool = False
45 ) -> Callable[[str, list[str]], set[str]]:
46     """
47     The default filter function used by `create_zipfile`.
48 
49     This function filters out several modules that are:
50 
51     - not supported in Pyodide due to browser limitations (e.g. `tkinter`)
52     - unvendored from the standard library (e.g. `sqlite3`)
53     """
54 
55     def _should_skip(path: Path) -> bool:
56         """Skip common files that are not needed in the zip file."""
57         name = path.name
58 
59         if path.is_dir() and name in ("__pycache__", "dist"):
60             return True
61 
62         if path.is_dir() and name.endswith((".egg-info", ".dist-info")):
63             return True
64 
65         if path.is_file() and name in (
66             "LICENSE",
67             "LICENSE.txt",
68             "setup.py",
69             ".gitignore",
70         ):
71             return True
72 
73         if path.is_file() and name.endswith(("pyi", "toml", "cfg", "md", "rst")):
74             return True
75 
76         return False
77 
78     def filterfunc(path: Path | str, names: list[str]) -> set[str]:
79         filtered_files = {
80             (root / f).resolve() for f in REMOVED_FILES + UNVENDORED_FILES
81         }
82 
83         # We have JS implementations of these modules, so we don't need to
84         # include the Python ones. Checking the name of the root directory
85         # is a bit of a hack, but it works...
86         if root.name.startswith("python3"):
87             filtered_files.update({root / f for f in JS_STUB_FILES})
88 
89         path = Path(path).resolve()
90 
91         if _should_skip(path):
92             return set(names)
93 
94         _names = []
95         for name in names:
96             fullpath = path / name
97 
98             if _should_skip(fullpath) or fullpath in filtered_files:
99                 if verbose:
100                     print(f"Skipping {fullpath}")
101 
102                 _names.append(name)
103 
104         return set(_names)
105 
106     return filterfunc
107 
108 
109 def create_zipfile(
110     libdirs: list[Path],
111     output: Path | str = "python",
112     pycompile: bool = False,
113     filterfunc: Callable[[str, list[str]], set[str]] | None = None,
114     compression_level: int = 6,
115 ) -> None:
116     """
117     Bundle Python standard libraries into a zip file.
118 
119     The basic idea of this function is similar to the standard library's
120     {ref}`zipfile.PyZipFile` class.
121 
122     However, we need some additional functionality for Pyodide. For example:
123 
124     - We need to remove some unvendored modules, e.g. `sqlite3`
125     - We need an option to "not" compile the files in the zip file
126 
127     hence this function.
128 
129     Parameters
130     ----------
131     libdirs
132         List of paths to the directory containing the Python standard library or extra packages.
133 
134     output
135         Path to the output zip file. Defaults to python.zip.
136 
137     pycompile
138         Whether to compile the .py files into .pyc, by default False
139 
140     filterfunc
141         A function that filters the files to be included in the zip file.
142         This function will be passed to {ref}`shutil.copytree` 's ignore argument.
143         By default, Pyodide's default filter function is used.
144 
145     compression_level
146         Level of zip compression to apply. 0 means no compression. If a strictly
147         positive integer is provided, ZIP_DEFLATED option is used.
148 
149     Returns
150     -------
151     BytesIO
152         A BytesIO object containing the zip file.
153     """
154 
155     archive = Path(output)
156 
157     with TemporaryDirectory() as temp_dir_str:
158         temp_dir = Path(temp_dir_str)
159 
160         for libdir in libdirs:
161             libdir = Path(libdir)
162 
163             if filterfunc is None:
164                 _filterfunc = default_filterfunc(libdir)
165 
166             shutil.copytree(libdir, temp_dir, ignore=_filterfunc, dirs_exist_ok=True)
167 
168         make_zip_archive(
169             archive,
170             temp_dir,
171             compression_level=compression_level,
172         )
173 
174     if pycompile:
175         _compile(
176             archive,
177             archive,
178             verbose=False,
179             keep=False,
180             compression_level=compression_level,
181         )
182
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pyodide-build/pyodide_build/pyzip.py b/pyodide-build/pyodide_build/pyzip.py
--- a/pyodide-build/pyodide_build/pyzip.py
+++ b/pyodide-build/pyodide_build/pyzip.py
@@ -28,7 +28,6 @@
 # These files are unvendored from the stdlib and can be loaded with `loadPackage`
 UNVENDORED_FILES = (
     "test/",
-    "distutils/",
     "sqlite3",
     "ssl.py",
     "lzma.py",

{"golden_diff": "diff --git a/pyodide-build/pyodide_build/pyzip.py b/pyodide-build/pyodide_build/pyzip.py\n--- a/pyodide-build/pyodide_build/pyzip.py\n+++ b/pyodide-build/pyodide_build/pyzip.py\n@@ -28,7 +28,6 @@\n # These files are unvendored from the stdlib and can be loaded with `loadPackage`\n UNVENDORED_FILES = (\n     \"test/\",\n-    \"distutils/\",\n     \"sqlite3\",\n     \"ssl.py\",\n     \"lzma.py\",\n", "issue": "Python 3.12 version\n## \ud83d\ude80 Feature\r\n\r\n<!-- A clear and concise description of the feature proposal -->\r\n\r\nHi, I tried [REPL](https://pyodide.org/en/stable/console.html), maybe it uses the latest 0.25.0, and I noticed that the python is 3.11.3.\r\n\r\nPython 3.12 has released for a few months with a lot of new features. Since there is no issue track the progress. So, I created this one.\r\n\r\n### Motivation\r\n\r\n<!-- Please outline the motivation for the proposal. Is your feature request related to a problem? e.g., I'm always frustrated when [...]. If this is related to another GitHub issue, please link here too -->\r\n\r\nN.A.\r\n\r\n### Pitch\r\n\r\n<!-- A clear and concise description of what you want to happen. -->\r\n\r\nN.A.\r\n\r\n### Alternatives\r\n\r\n<!-- A clear and concise description of any alternative solutions or features you've considered, if any. -->\r\n\r\nN.A.\r\n\r\n### Additional context\r\n\r\n<!-- Add any other context or screenshots about the feature request here. -->\r\n\r\nN.A.\n", "before_files": [{"content": "import shutil\nfrom collections.abc import Callable\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\n\nfrom ._py_compile import _compile\nfrom .common import make_zip_archive\n\n# These files are removed from the stdlib\nREMOVED_FILES = (\n    # package management\n    \"ensurepip/\",\n    \"venv/\",\n    # build system\n    \"lib2to3/\",\n    # other platforms\n    \"_osx_support.py\",\n    \"_aix_support.py\",\n    # Not supported by browser\n    \"curses/\",\n    \"dbm/\",\n    \"idlelib/\",\n    \"tkinter/\",\n    \"turtle.py\",\n    \"turtledemo\",\n)\n\n# These files are unvendored from the stdlib and can be loaded with `loadPackage`\nUNVENDORED_FILES = (\n    \"test/\",\n    \"distutils/\",\n    \"sqlite3\",\n    \"ssl.py\",\n    \"lzma.py\",\n    \"_pydecimal.py\",\n    \"pydoc_data\",\n)\n\n# We have JS implementations of these modules\nJS_STUB_FILES = (\"webbrowser.py\",)\n\n\ndef default_filterfunc(\n    root: Path, verbose: bool = False\n) -> Callable[[str, list[str]], set[str]]:\n    \"\"\"\n    The default filter function used by `create_zipfile`.\n\n    This function filters out several modules that are:\n\n    - not supported in Pyodide due to browser limitations (e.g. `tkinter`)\n    - unvendored from the standard library (e.g. `sqlite3`)\n    \"\"\"\n\n    def _should_skip(path: Path) -> bool:\n        \"\"\"Skip common files that are not needed in the zip file.\"\"\"\n        name = path.name\n\n        if path.is_dir() and name in (\"__pycache__\", \"dist\"):\n            return True\n\n        if path.is_dir() and name.endswith((\".egg-info\", \".dist-info\")):\n            return True\n\n        if path.is_file() and name in (\n            \"LICENSE\",\n            \"LICENSE.txt\",\n            \"setup.py\",\n            \".gitignore\",\n        ):\n            return True\n\n        if path.is_file() and name.endswith((\"pyi\", \"toml\", \"cfg\", \"md\", \"rst\")):\n            return True\n\n        return False\n\n    def filterfunc(path: Path | str, names: list[str]) -> set[str]:\n        filtered_files = {\n            (root / f).resolve() for f in REMOVED_FILES + UNVENDORED_FILES\n        }\n\n        # We have JS implementations of these modules, so we don't need to\n        # include the Python ones. Checking the name of the root directory\n        # is a bit of a hack, but it works...\n        if root.name.startswith(\"python3\"):\n            filtered_files.update({root / f for f in JS_STUB_FILES})\n\n        path = Path(path).resolve()\n\n        if _should_skip(path):\n            return set(names)\n\n        _names = []\n        for name in names:\n            fullpath = path / name\n\n            if _should_skip(fullpath) or fullpath in filtered_files:\n                if verbose:\n                    print(f\"Skipping {fullpath}\")\n\n                _names.append(name)\n\n        return set(_names)\n\n    return filterfunc\n\n\ndef create_zipfile(\n    libdirs: list[Path],\n    output: Path | str = \"python\",\n    pycompile: bool = False,\n    filterfunc: Callable[[str, list[str]], set[str]] | None = None,\n    compression_level: int = 6,\n) -> None:\n    \"\"\"\n    Bundle Python standard libraries into a zip file.\n\n    The basic idea of this function is similar to the standard library's\n    {ref}`zipfile.PyZipFile` class.\n\n    However, we need some additional functionality for Pyodide. For example:\n\n    - We need to remove some unvendored modules, e.g. `sqlite3`\n    - We need an option to \"not\" compile the files in the zip file\n\n    hence this function.\n\n    Parameters\n    ----------\n    libdirs\n        List of paths to the directory containing the Python standard library or extra packages.\n\n    output\n        Path to the output zip file. Defaults to python.zip.\n\n    pycompile\n        Whether to compile the .py files into .pyc, by default False\n\n    filterfunc\n        A function that filters the files to be included in the zip file.\n        This function will be passed to {ref}`shutil.copytree` 's ignore argument.\n        By default, Pyodide's default filter function is used.\n\n    compression_level\n        Level of zip compression to apply. 0 means no compression. If a strictly\n        positive integer is provided, ZIP_DEFLATED option is used.\n\n    Returns\n    -------\n    BytesIO\n        A BytesIO object containing the zip file.\n    \"\"\"\n\n    archive = Path(output)\n\n    with TemporaryDirectory() as temp_dir_str:\n        temp_dir = Path(temp_dir_str)\n\n        for libdir in libdirs:\n            libdir = Path(libdir)\n\n            if filterfunc is None:\n                _filterfunc = default_filterfunc(libdir)\n\n            shutil.copytree(libdir, temp_dir, ignore=_filterfunc, dirs_exist_ok=True)\n\n        make_zip_archive(\n            archive,\n            temp_dir,\n            compression_level=compression_level,\n        )\n\n    if pycompile:\n        _compile(\n            archive,\n            archive,\n            verbose=False,\n            keep=False,\n            compression_level=compression_level,\n        )\n", "path": "pyodide-build/pyodide_build/pyzip.py"}], "after_files": [{"content": "import shutil\nfrom collections.abc import Callable\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\n\nfrom ._py_compile import _compile\nfrom .common import make_zip_archive\n\n# These files are removed from the stdlib\nREMOVED_FILES = (\n    # package management\n    \"ensurepip/\",\n    \"venv/\",\n    # build system\n    \"lib2to3/\",\n    # other platforms\n    \"_osx_support.py\",\n    \"_aix_support.py\",\n    # Not supported by browser\n    \"curses/\",\n    \"dbm/\",\n    \"idlelib/\",\n    \"tkinter/\",\n    \"turtle.py\",\n    \"turtledemo\",\n)\n\n# These files are unvendored from the stdlib and can be loaded with `loadPackage`\nUNVENDORED_FILES = (\n    \"test/\",\n    \"sqlite3\",\n    \"ssl.py\",\n    \"lzma.py\",\n    \"_pydecimal.py\",\n    \"pydoc_data\",\n)\n\n# We have JS implementations of these modules\nJS_STUB_FILES = (\"webbrowser.py\",)\n\n\ndef default_filterfunc(\n    root: Path, verbose: bool = False\n) -> Callable[[str, list[str]], set[str]]:\n    \"\"\"\n    The default filter function used by `create_zipfile`.\n\n    This function filters out several modules that are:\n\n    - not supported in Pyodide due to browser limitations (e.g. `tkinter`)\n    - unvendored from the standard library (e.g. `sqlite3`)\n    \"\"\"\n\n    def _should_skip(path: Path) -> bool:\n        \"\"\"Skip common files that are not needed in the zip file.\"\"\"\n        name = path.name\n\n        if path.is_dir() and name in (\"__pycache__\", \"dist\"):\n            return True\n\n        if path.is_dir() and name.endswith((\".egg-info\", \".dist-info\")):\n            return True\n\n        if path.is_file() and name in (\n            \"LICENSE\",\n            \"LICENSE.txt\",\n            \"setup.py\",\n            \".gitignore\",\n        ):\n            return True\n\n        if path.is_file() and name.endswith((\"pyi\", \"toml\", \"cfg\", \"md\", \"rst\")):\n            return True\n\n        return False\n\n    def filterfunc(path: Path | str, names: list[str]) -> set[str]:\n        filtered_files = {\n            (root / f).resolve() for f in REMOVED_FILES + UNVENDORED_FILES\n        }\n\n        # We have JS implementations of these modules, so we don't need to\n        # include the Python ones. Checking the name of the root directory\n        # is a bit of a hack, but it works...\n        if root.name.startswith(\"python3\"):\n            filtered_files.update({root / f for f in JS_STUB_FILES})\n\n        path = Path(path).resolve()\n\n        if _should_skip(path):\n            return set(names)\n\n        _names = []\n        for name in names:\n            fullpath = path / name\n\n            if _should_skip(fullpath) or fullpath in filtered_files:\n                if verbose:\n                    print(f\"Skipping {fullpath}\")\n\n                _names.append(name)\n\n        return set(_names)\n\n    return filterfunc\n\n\ndef create_zipfile(\n    libdirs: list[Path],\n    output: Path | str = \"python\",\n    pycompile: bool = False,\n    filterfunc: Callable[[str, list[str]], set[str]] | None = None,\n    compression_level: int = 6,\n) -> None:\n    \"\"\"\n    Bundle Python standard libraries into a zip file.\n\n    The basic idea of this function is similar to the standard library's\n    {ref}`zipfile.PyZipFile` class.\n\n    However, we need some additional functionality for Pyodide. For example:\n\n    - We need to remove some unvendored modules, e.g. `sqlite3`\n    - We need an option to \"not\" compile the files in the zip file\n\n    hence this function.\n\n    Parameters\n    ----------\n    libdirs\n        List of paths to the directory containing the Python standard library or extra packages.\n\n    output\n        Path to the output zip file. Defaults to python.zip.\n\n    pycompile\n        Whether to compile the .py files into .pyc, by default False\n\n    filterfunc\n        A function that filters the files to be included in the zip file.\n        This function will be passed to {ref}`shutil.copytree` 's ignore argument.\n        By default, Pyodide's default filter function is used.\n\n    compression_level\n        Level of zip compression to apply. 0 means no compression. If a strictly\n        positive integer is provided, ZIP_DEFLATED option is used.\n\n    Returns\n    -------\n    BytesIO\n        A BytesIO object containing the zip file.\n    \"\"\"\n\n    archive = Path(output)\n\n    with TemporaryDirectory() as temp_dir_str:\n        temp_dir = Path(temp_dir_str)\n\n        for libdir in libdirs:\n            libdir = Path(libdir)\n\n            if filterfunc is None:\n                _filterfunc = default_filterfunc(libdir)\n\n            shutil.copytree(libdir, temp_dir, ignore=_filterfunc, dirs_exist_ok=True)\n\n        make_zip_archive(\n            archive,\n            temp_dir,\n            compression_level=compression_level,\n        )\n\n    if pycompile:\n        _compile(\n            archive,\n            archive,\n            verbose=False,\n            keep=False,\n            compression_level=compression_level,\n        )\n", "path": "pyodide-build/pyodide_build/pyzip.py"}]}