diff --git a/Misc/NEWS.d/next/Build/2022-09-17-11-19-24.gh-issue-96883.p_gr62.rst b/Misc/NEWS.d/next/Build/2022-09-17-11-19-24.gh-issue-96883.p_gr62.rst new file mode 100644 index 00000000000..2258ce8ab9d --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-09-17-11-19-24.gh-issue-96883.p_gr62.rst @@ -0,0 +1,2 @@ +``wasm32-emscripten`` builds for browsers now include +:mod:`concurrent.futures` for :mod:`asyncio` and :mod:`unittest.mock`. diff --git a/Tools/wasm/wasm_assets.py b/Tools/wasm/wasm_assets.py index a35acfd9387..98a2841ce2b 100755 --- a/Tools/wasm/wasm_assets.py +++ b/Tools/wasm/wasm_assets.py @@ -58,6 +58,8 @@ OMIT_FILES = ( # Pure Python implementations of C extensions "_pydecimal.py", "_pyio.py", + # concurrent threading + "concurrent/futures/thread.py", # Misc unused or large files "pydoc_data/", "msilib/", @@ -98,13 +100,12 @@ OMIT_MODULE_FILES = { "_dbm": ["dbm/ndbm.py"], "_gdbm": ["dbm/gnu.py"], "_json": ["json/"], - "_multiprocessing": ["concurrent/", "multiprocessing/"], + "_multiprocessing": ["concurrent/futures/process.py", "multiprocessing/"], "pyexpat": ["xml/", "xmlrpc/"], "readline": ["rlcompleter.py"], "_sqlite3": ["sqlite3/"], "_ssl": ["ssl.py"], "_tkinter": ["idlelib/", "tkinter/", "turtle.py", "turtledemo/"], - "_zoneinfo": ["zoneinfo/"], } @@ -117,21 +118,18 @@ SYSCONFIG_NAMES = ( def get_builddir(args: argparse.Namespace) -> pathlib.Path: - """Get builddir path from pybuilddir.txt - """ + """Get builddir path from pybuilddir.txt""" with open("pybuilddir.txt", encoding="utf-8") as f: builddir = f.read() return pathlib.Path(builddir) def get_sysconfigdata(args: argparse.Namespace) -> pathlib.Path: - """Get path to sysconfigdata relative to build root - """ + """Get path to sysconfigdata relative to build root""" data_name = sysconfig._get_sysconfigdata_name() if not data_name.startswith(SYSCONFIG_NAMES): raise ValueError( - f"Invalid sysconfig data name '{data_name}'.", - SYSCONFIG_NAMES + f"Invalid sysconfig data name '{data_name}'.", SYSCONFIG_NAMES ) filename = data_name + ".py" return args.builddir / filename @@ -142,20 +140,26 @@ def create_stdlib_zip( *, optimize: int = 0, ) -> None: + def filterfunc(filename: str) -> bool: + pathname = pathlib.Path(filename).resolve() + return pathname not in args.omit_files_absolute + with zipfile.PyZipFile( - args.wasm_stdlib_zip, mode="w", compression=args.compression, optimize=optimize + args.wasm_stdlib_zip, + mode="w", + compression=args.compression, + optimize=optimize, ) as pzf: if args.compresslevel is not None: pzf.compresslevel = args.compresslevel pzf.writepy(args.sysconfig_data) for entry in sorted(args.srcdir_lib.iterdir()): + entry = entry.resolve() if entry.name == "__pycache__": continue - if entry in args.omit_files_absolute: - continue if entry.name.endswith(".py") or entry.is_dir(): # writepy() writes .pyc files (bytecode). - pzf.writepy(entry) + pzf.writepy(entry, filterfunc=filterfunc) def detect_extension_modules(args: argparse.Namespace): @@ -236,7 +240,9 @@ def main(): if not extmods.get(modname): omit_files.extend(modfiles) - args.omit_files_absolute = {args.srcdir_lib / name for name in omit_files} + args.omit_files_absolute = { + (args.srcdir_lib / name).resolve() for name in omit_files + } # Empty, unused directory for dynamic libs, but required for site initialization. args.wasm_dynload.mkdir(parents=True, exist_ok=True)