https://github.com/python/cpython/commit/56146059d3479c04a65805f5326e7200cf7040b7
commit: 56146059d3479c04a65805f5326e7200cf7040b7
branch: 3.13
author: Hugo van Kemenade <[email protected]>
committer: hugovk <[email protected]>
date: 2025-11-04T18:17:57Z
summary:

[3.13] gh-139590: Stricter `ruff` rules for `Tools/wasm` (GH-139752) (#140986)

Co-authored-by: sobolevn <[email protected]>

files:
D Tools/wasm/mypy.ini
M .github/workflows/mypy.yml
M .pre-commit-config.yaml
M Tools/wasm/.ruff.toml
M Tools/wasm/wasi.py
M Tools/wasm/wasm_assets.py
M Tools/wasm/wasm_build.py

diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index ba30e027d98b1b..ccf3ca11293dd3 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -27,7 +27,6 @@ on:
       - "Tools/jit/**"
       - "Tools/peg_generator/**"
       - "Tools/requirements-dev.txt"
-      - "Tools/wasm/**"
   workflow_dispatch:
 
 permissions:
@@ -59,7 +58,6 @@ jobs:
           "Tools/clinic",
           "Tools/jit",
           "Tools/peg_generator",
-          "Tools/wasm",
         ]
     steps:
       - uses: actions/checkout@v4
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index aa5dab0ad16dcb..6347ffb3e119d8 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -22,6 +22,10 @@ repos:
         name: Run Ruff (lint) on Tools/peg_generator/
         args: [--exit-non-zero-on-fix, --config=Tools/peg_generator/.ruff.toml]
         files: ^Tools/peg_generator/
+      - id: ruff-check
+        name: Run Ruff (lint) on Tools/wasm/
+        args: [--exit-non-zero-on-fix, --config=Tools/wasm/.ruff.toml]
+        files: ^Tools/wasm/
       - id: ruff-format
         name: Run Ruff (format) on Doc/
         args: [--check]
diff --git a/Tools/wasm/.ruff.toml b/Tools/wasm/.ruff.toml
index aabcf8dc4f502e..3d8e59fa3f22c4 100644
--- a/Tools/wasm/.ruff.toml
+++ b/Tools/wasm/.ruff.toml
@@ -22,7 +22,4 @@ select = [
 ]
 ignore = [
     "E501",    # Line too long
-    "F541",    # f-string without any placeholders
-    "PYI024",  # Use `typing.NamedTuple` instead of `collections.namedtuple`
-    "PYI025",  # Use `from collections.abc import Set as AbstractSet`
 ]
diff --git a/Tools/wasm/mypy.ini b/Tools/wasm/mypy.ini
deleted file mode 100644
index 4de0a30c260f5f..00000000000000
--- a/Tools/wasm/mypy.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[mypy]
-files = Tools/wasm/wasm_*.py
-pretty = True
-show_traceback = True
-
-# Make sure the wasm can be run using Python 3.8:
-python_version = 3.8
-
-# Be strict...
-strict = True
-enable_error_code = truthy-bool,ignore-without-code
diff --git a/Tools/wasm/wasi.py b/Tools/wasm/wasi.py
index 34051bd7351a37..3f76900fc60aa5 100644
--- a/Tools/wasm/wasi.py
+++ b/Tools/wasm/wasi.py
@@ -16,14 +16,13 @@
 import sysconfig
 import tempfile
 
-
 CHECKOUT = pathlib.Path(__file__).parent.parent.parent
 
 CROSS_BUILD_DIR = CHECKOUT / "cross-build"
 BUILD_DIR = CROSS_BUILD_DIR / "build"
 
 LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local"
-LOCAL_SETUP_MARKER = "# Generated by Tools/wasm/wasi.py\n".encode("utf-8")
+LOCAL_SETUP_MARKER = b"# Generated by Tools/wasm/wasi.py\n"
 
 WASMTIME_VAR_NAME = "WASMTIME"
 WASMTIME_HOST_RUNNER_VAR = f"{{{WASMTIME_VAR_NAME}}}"
@@ -84,7 +83,7 @@ def wrapper(context):
                 and getattr(context, "clean", False)
                 and working_dir.exists()
             ):
-                print(f"🚮 Deleting directory (--clean)...")
+                print("🚮 Deleting directory (--clean)...")
                 shutil.rmtree(working_dir)
 
             working_dir.mkdir(parents=True, exist_ok=True)
@@ -372,7 +371,7 @@ def main():
     make_host = subcommands.add_parser(
         "make-host", help="Run `make` for the host/WASI"
     )
-    clean = subcommands.add_parser(
+    subcommands.add_parser(
         "clean", help="Delete files and directories created by this script"
     )
     for subcommand in (
diff --git a/Tools/wasm/wasm_assets.py b/Tools/wasm/wasm_assets.py
index 6cfb73a9525779..8229ab62f2726b 100755
--- a/Tools/wasm/wasm_assets.py
+++ b/Tools/wasm/wasm_assets.py
@@ -16,7 +16,6 @@
 import sys
 import sysconfig
 import zipfile
-from typing import Dict
 
 # source directory
 SRCDIR = pathlib.Path(__file__).parent.parent.parent.absolute()
@@ -147,7 +146,7 @@ def filterfunc(filename: str) -> bool:
                 pzf.writepy(entry, filterfunc=filterfunc)
 
 
-def detect_extension_modules(args: argparse.Namespace) -> Dict[str, bool]:
+def detect_extension_modules(args: argparse.Namespace) -> dict[str, bool]:
     modules = {}
 
     # disabled by Modules/Setup.local ?
@@ -162,7 +161,7 @@ def detect_extension_modules(args: argparse.Namespace) -> 
Dict[str, bool]:
     # disabled by configure?
     with open(args.sysconfig_data) as f:
         data = f.read()
-    loc: Dict[str, Dict[str, str]] = {}
+    loc: dict[str, dict[str, str]] = {}
     exec(data, globals(), loc)
 
     for key, value in loc["build_time_vars"].items():
diff --git a/Tools/wasm/wasm_build.py b/Tools/wasm/wasm_build.py
index 5cc1c06f4a2796..d9f7dd92ba1f5d 100755
--- a/Tools/wasm/wasm_build.py
+++ b/Tools/wasm/wasm_build.py
@@ -23,8 +23,8 @@
 """
 
 import argparse
-import enum
 import dataclasses
+import enum
 import logging
 import os
 import pathlib
@@ -39,18 +39,12 @@
 import time
 import warnings
 import webbrowser
+from collections.abc import Callable, Iterable
 
 # for Python 3.8
 from typing import (
-    cast,
     Any,
-    Callable,
-    Dict,
-    Iterable,
-    List,
-    Optional,
-    Tuple,
-    Union,
+    cast,
 )
 
 logger = logging.getLogger("wasm_build")
@@ -122,7 +116,7 @@
 
 def parse_emconfig(
     emconfig: pathlib.Path = EM_CONFIG,
-) -> Tuple[pathlib.Path, pathlib.Path]:
+) -> tuple[pathlib.Path, pathlib.Path]:
     """Parse EM_CONFIG file and lookup EMSCRIPTEN_ROOT and NODE_JS.
 
     The ".emscripten" config file is a Python snippet that uses "EM_CONFIG"
@@ -134,7 +128,7 @@ def parse_emconfig(
     with open(emconfig, encoding="utf-8") as f:
         code = f.read()
     # EM_CONFIG file is a Python snippet
-    local: Dict[str, Any] = {}
+    local: dict[str, Any] = {}
     exec(code, globals(), local)
     emscripten_root = pathlib.Path(local["EMSCRIPTEN_ROOT"])
     node_js = pathlib.Path(local["NODE_JS"])
@@ -192,16 +186,16 @@ class Platform:
 
     name: str
     pythonexe: str
-    config_site: Optional[pathlib.PurePath]
-    configure_wrapper: Optional[pathlib.Path]
-    make_wrapper: Optional[pathlib.PurePath]
-    environ: Dict[str, Any]
+    config_site: pathlib.PurePath | None
+    configure_wrapper: pathlib.Path | None
+    make_wrapper: pathlib.PurePath | None
+    environ: dict[str, Any]
     check: Callable[[], None]
     # Used for build_emports().
-    ports: Optional[pathlib.PurePath]
-    cc: Optional[pathlib.PurePath]
+    ports: pathlib.PurePath | None
+    cc: pathlib.PurePath | None
 
-    def getenv(self, profile: "BuildProfile") -> Dict[str, Any]:
+    def getenv(self, profile: "BuildProfile") -> dict[str, Any]:
         return self.environ.copy()
 
 
@@ -264,7 +258,7 @@ def _check_emscripten() -> None:
         # git / upstream / tot-upstream installation
         version = version[:-4]
     version_tuple = cast(
-        Tuple[int, int, int], tuple(int(v) for v in version.split("."))
+        tuple[int, int, int], tuple(int(v) for v in version.split("."))
     )
     if version_tuple < EMSDK_MIN_VERSION:
         raise ConditionError(
@@ -388,7 +382,7 @@ def get_extra_paths(self) -> Iterable[pathlib.PurePath]:
             return []
 
     @property
-    def emport_args(self) -> List[str]:
+    def emport_args(self) -> list[str]:
         """Host-specific port args (Emscripten)."""
         cls = type(self)
         if self is cls.wasm64_emscripten:
@@ -399,7 +393,7 @@ def emport_args(self) -> List[str]:
             return []
 
     @property
-    def embuilder_args(self) -> List[str]:
+    def embuilder_args(self) -> list[str]:
         """Host-specific embuilder args (Emscripten)."""
         cls = type(self)
         if self is cls.wasm64_emscripten:
@@ -422,7 +416,7 @@ def is_browser(self) -> bool:
         return self in {cls.browser, cls.browser_debug}
 
     @property
-    def emport_args(self) -> List[str]:
+    def emport_args(self) -> list[str]:
         """Target-specific port args."""
         cls = type(self)
         if self in {cls.browser_debug, cls.node_debug}:
@@ -448,9 +442,9 @@ class BuildProfile:
     name: str
     support_level: SupportLevel
     host: Host
-    target: Union[EmscriptenTarget, None] = None
-    dynamic_linking: Union[bool, None] = None
-    pthreads: Union[bool, None] = None
+    target: EmscriptenTarget | None = None
+    dynamic_linking: bool | None = None
+    pthreads: bool | None = None
     default_testopts: str = "-j2"
 
     @property
@@ -474,7 +468,7 @@ def makefile(self) -> pathlib.Path:
         return self.builddir / "Makefile"
 
     @property
-    def configure_cmd(self) -> List[str]:
+    def configure_cmd(self) -> list[str]:
         """Generate configure command"""
         # use relative path, so WASI tests can find lib prefix.
         # pathlib.Path.relative_to() does not work here.
@@ -509,7 +503,7 @@ def configure_cmd(self) -> List[str]:
         return cmd
 
     @property
-    def make_cmd(self) -> List[str]:
+    def make_cmd(self) -> list[str]:
         """Generate make command"""
         cmd = ["make"]
         platform = self.host.platform
@@ -517,7 +511,7 @@ def make_cmd(self) -> List[str]:
             cmd.insert(0, os.fspath(platform.make_wrapper))
         return cmd
 
-    def getenv(self) -> Dict[str, Any]:
+    def getenv(self) -> dict[str, Any]:
         """Generate environ dict for platform"""
         env = os.environ.copy()
         if hasattr(os, "process_cpu_count"):
@@ -531,7 +525,7 @@ def getenv(self) -> Dict[str, Any]:
                 env.pop(key, None)
             elif key == "PATH":
                 # list of path items, prefix with extra paths
-                new_path: List[pathlib.PurePath] = []
+                new_path: list[pathlib.PurePath] = []
                 new_path.extend(self.host.get_extra_paths())
                 new_path.extend(value)
                 env[key] = os.pathsep.join(os.fspath(p) for p in new_path)
@@ -549,7 +543,7 @@ def _run_cmd(
         self,
         cmd: Iterable[str],
         args: Iterable[str] = (),
-        cwd: Optional[pathlib.Path] = None,
+        cwd: pathlib.Path | None = None,
     ) -> int:
         cmd = list(cmd)
         cmd.extend(args)
@@ -587,7 +581,7 @@ def run_pythoninfo(self, *args: str) -> int:
         self._check_execute()
         return self.run_make("pythoninfo", *args)
 
-    def run_test(self, target: str, testopts: Optional[str] = None) -> int:
+    def run_test(self, target: str, testopts: str | None = None) -> int:
         """Run buildbottests"""
         self._check_execute()
         if testopts is None:
@@ -823,10 +817,8 @@ def build_emports(self, force: bool = False) -> None:
 )
 
 # Don't list broken and experimental variants in help
-platforms_choices = list(p.name for p in _profiles) + ["cleanall"]
-platforms_help = list(p.name for p in _profiles if p.support_level) + [
-    "cleanall"
-]
+platforms_choices = [p.name for p in _profiles] + ["cleanall"]
+platforms_help = [p.name for p in _profiles if p.support_level] + ["cleanall"]
 parser.add_argument(
     "platform",
     metavar="PLATFORM",
@@ -834,18 +826,18 @@ def build_emports(self, force: bool = False) -> None:
     choices=platforms_choices,
 )
 
-ops = dict(
-    build="auto build (build 'build' Python, emports, configure, compile)",
-    configure="run ./configure",
-    compile="run 'make all'",
-    pythoninfo="run 'make pythoninfo'",
-    test="run 'make buildbottest TESTOPTS=...' (supports parallel tests)",
-    hostrunnertest="run 'make hostrunnertest TESTOPTS=...'",
-    repl="start interactive REPL / webserver + browser session",
-    clean="run 'make clean'",
-    cleanall="remove all build directories",
-    emports="build Emscripten port with embuilder (only Emscripten)",
-)
+ops = {
+    "build": "auto build (build 'build' Python, emports, configure, compile)",
+    "configure": "run ./configure",
+    "compile": "run 'make all'",
+    "pythoninfo": "run 'make pythoninfo'",
+    "test": "run 'make buildbottest TESTOPTS=...' (supports parallel tests)",
+    "hostrunnertest": "run 'make hostrunnertest TESTOPTS=...'",
+    "repl": "start interactive REPL / webserver + browser session",
+    "clean": "run 'make clean'",
+    "cleanall": "remove all build directories",
+    "emports": "build Emscripten port with embuilder (only Emscripten)",
+}
 ops_help = "\n".join(f"{op:16s} {help}" for op, help in ops.items())
 parser.add_argument(
     "ops",

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to