]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-141691: Apply ruff rules to Apple folder. (GH-141694) (#141728)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 18 Nov 2025 23:14:31 +0000 (00:14 +0100)
committerGitHub <noreply@github.com>
Tue, 18 Nov 2025 23:14:31 +0000 (23:14 +0000)
Add ruff configuration to run over the Apple build tooling and testbed script.
(cherry picked from commit 17636ba48ce535fc1b1926c0bab26339da50631a)

Co-authored-by: Russell Keith-Magee <russell@keith-magee.com>
.pre-commit-config.yaml
Apple/.ruff.toml [new file with mode: 0644]
Apple/__main__.py
Apple/testbed/__main__.py

index b0311f052798adc58618f4bc351fe3c85c80c0f3..c5767ee841eb0d3be0aeb41985a06a7a71798e4f 100644 (file)
@@ -2,6 +2,10 @@ repos:
   - repo: https://github.com/astral-sh/ruff-pre-commit
     rev: v0.13.2
     hooks:
+      - id: ruff-check
+        name: Run Ruff (lint) on Apple/
+        args: [--exit-non-zero-on-fix, --config=Apple/.ruff.toml]
+        files: ^Apple/
       - id: ruff-check
         name: Run Ruff (lint) on Doc/
         args: [--exit-non-zero-on-fix]
@@ -30,6 +34,10 @@ repos:
         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 Apple/
+        args: [--exit-non-zero-on-fix, --config=Apple/.ruff.toml]
+        files: ^Apple
       - id: ruff-format
         name: Run Ruff (format) on Doc/
         args: [--check]
diff --git a/Apple/.ruff.toml b/Apple/.ruff.toml
new file mode 100644 (file)
index 0000000..4cdc39e
--- /dev/null
@@ -0,0 +1,22 @@
+extend = "../.ruff.toml"  # Inherit the project-wide settings
+
+[format]
+preview = true
+docstring-code-format = true
+
+[lint]
+select = [
+    "C4",      # flake8-comprehensions
+    "E",       # pycodestyle
+    "F",       # pyflakes
+    "I",       # isort
+    "ISC",     # flake8-implicit-str-concat
+    "LOG",     # flake8-logging
+    "PGH",     # pygrep-hooks
+    "PT",      # flake8-pytest-style
+    "PYI",     # flake8-pyi
+    "RUF100",  # Ban unused `# noqa` comments
+    "UP",      # pyupgrade
+    "W",       # pycodestyle
+    "YTT",     # flake8-2020
+]
index e76fc3517987073100e7abd77df26595c91d10b7..1c588c23d6b5d1a0568f1efc1359310ff8782c8a 100644 (file)
@@ -46,13 +46,12 @@ import subprocess
 import sys
 import sysconfig
 import time
-from collections.abc import Sequence
+from collections.abc import Callable, Sequence
 from contextlib import contextmanager
 from datetime import datetime, timezone
 from os.path import basename, relpath
 from pathlib import Path
 from subprocess import CalledProcessError
-from typing import Callable
 
 EnvironmentT = dict[str, str]
 ArgsT = Sequence[str | Path]
@@ -140,17 +139,15 @@ def print_env(env: EnvironmentT) -> None:
 def apple_env(host: str) -> EnvironmentT:
     """Construct an Apple development environment for the given host."""
     env = {
-        "PATH": ":".join(
-            [
-                str(PYTHON_DIR / "Apple/iOS/Resources/bin"),
-                str(subdir(host) / "prefix"),
-                "/usr/bin",
-                "/bin",
-                "/usr/sbin",
-                "/sbin",
-                "/Library/Apple/usr/bin",
-            ]
-        ),
+        "PATH": ":".join([
+            str(PYTHON_DIR / "Apple/iOS/Resources/bin"),
+            str(subdir(host) / "prefix"),
+            "/usr/bin",
+            "/bin",
+            "/usr/sbin",
+            "/sbin",
+            "/Library/Apple/usr/bin",
+        ]),
     }
 
     return env
@@ -196,14 +193,10 @@ def clean(context: argparse.Namespace, target: str = "all") -> None:
         paths.append(target)
 
     if target in {"all", "hosts", "test"}:
-        paths.extend(
-            [
-                path.name
-                for path in CROSS_BUILD_DIR.glob(
-                    f"{context.platform}-testbed.*"
-                )
-            ]
-        )
+        paths.extend([
+            path.name
+            for path in CROSS_BUILD_DIR.glob(f"{context.platform}-testbed.*")
+        ])
 
     for path in paths:
         delete_path(path)
@@ -352,18 +345,16 @@ def download(url: str, target_dir: Path) -> Path:
 
     out_path = target_path / basename(url)
     if not Path(out_path).is_file():
-        run(
-            [
-                "curl",
-                "-Lf",
-                "--retry",
-                "5",
-                "--retry-all-errors",
-                "-o",
-                out_path,
-                url,
-            ]
-        )
+        run([
+            "curl",
+            "-Lf",
+            "--retry",
+            "5",
+            "--retry-all-errors",
+            "-o",
+            out_path,
+            url,
+        ])
     else:
         print(f"Using cached version of {basename(url)}")
     return out_path
@@ -468,8 +459,7 @@ def package_version(prefix_path: Path) -> str:
 
 
 def lib_platform_files(dirname, names):
-    """A file filter that ignores platform-specific files in the lib directory.
-    """
+    """A file filter that ignores platform-specific files in lib."""
     path = Path(dirname)
     if (
         path.parts[-3] == "lib"
@@ -478,7 +468,7 @@ def lib_platform_files(dirname, names):
     ):
         return names
     elif path.parts[-2] == "lib" and path.parts[-1].startswith("python"):
-        ignored_names = set(
+        ignored_names = {
             name
             for name in names
             if (
@@ -486,7 +476,7 @@ def lib_platform_files(dirname, names):
                 or name.startswith("_sysconfig_vars_")
                 or name == "build-details.json"
             )
-        )
+        }
     else:
         ignored_names = set()
 
@@ -499,7 +489,9 @@ def lib_non_platform_files(dirname, names):
     """
     path = Path(dirname)
     if path.parts[-2] == "lib" and path.parts[-1].startswith("python"):
-        return set(names) - lib_platform_files(dirname, names) - {"lib-dynload"}
+        return (
+            set(names) - lib_platform_files(dirname, names) - {"lib-dynload"}
+        )
     else:
         return set()
 
@@ -514,7 +506,8 @@ def create_xcframework(platform: str) -> str:
         package_path.mkdir()
     except FileExistsError:
         raise RuntimeError(
-            f"{platform} XCframework already exists; do you need to run with --clean?"
+            f"{platform} XCframework already exists; do you need to run "
+            "with --clean?"
         ) from None
 
     frameworks = []
@@ -607,7 +600,7 @@ def create_xcframework(platform: str) -> str:
         print(f" - {slice_name} binaries")
         shutil.copytree(first_path / "bin", slice_path / "bin")
 
-        # Copy the include path (this will be a symlink to the framework headers)
+        # Copy the include path (a symlink to the framework headers)
         print(f" - {slice_name} include files")
         shutil.copytree(
             first_path / "include",
@@ -659,7 +652,8 @@ def create_xcframework(platform: str) -> str:
             # statically link those libraries into a Framework, you become
             # responsible for providing a privacy manifest for that framework.
             xcprivacy_file = {
-                "OpenSSL": subdir(host_triple) / "prefix/share/OpenSSL.xcprivacy"
+                "OpenSSL": subdir(host_triple)
+                / "prefix/share/OpenSSL.xcprivacy"
             }
             print(f"   - {multiarch} xcprivacy files")
             for module, lib in [
@@ -669,7 +663,8 @@ def create_xcframework(platform: str) -> str:
                 shutil.copy(
                     xcprivacy_file[lib],
                     slice_path
-                    / f"lib-{arch}/python{version_tag}/lib-dynload/{module}.xcprivacy",
+                    / f"lib-{arch}/python{version_tag}"
+                    / f"lib-dynload/{module}.xcprivacy",
                 )
 
     print(" - build tools")
@@ -692,18 +687,16 @@ def package(context: argparse.Namespace) -> None:
 
         # Clone testbed
         print()
-        run(
-            [
-                sys.executable,
-                "Apple/testbed",
-                "clone",
-                "--platform",
-                context.platform,
-                "--framework",
-                CROSS_BUILD_DIR / context.platform / "Python.xcframework",
-                CROSS_BUILD_DIR / context.platform / "testbed",
-            ]
-        )
+        run([
+            sys.executable,
+            "Apple/testbed",
+            "clone",
+            "--platform",
+            context.platform,
+            "--framework",
+            CROSS_BUILD_DIR / context.platform / "Python.xcframework",
+            CROSS_BUILD_DIR / context.platform / "testbed",
+        ])
 
         # Build the final archive
         archive_name = (
@@ -757,7 +750,7 @@ def build(context: argparse.Namespace, host: str | None = None) -> None:
         package(context)
 
 
-def test(context: argparse.Namespace, host: str | None = None) -> None:
+def test(context: argparse.Namespace, host: str | None = None) -> None:  # noqa: PT028
     """The implementation of the "test" command."""
     if host is None:
         host = context.host
@@ -795,18 +788,16 @@ def test(context: argparse.Namespace, host: str | None = None) -> None:
                     / f"Frameworks/{apple_multiarch(host)}"
                 )
 
-        run(
-            [
-                sys.executable,
-                "Apple/testbed",
-                "clone",
-                "--platform",
-                context.platform,
-                "--framework",
-                framework_path,
-                testbed_dir,
-            ]
-        )
+        run([
+            sys.executable,
+            "Apple/testbed",
+            "clone",
+            "--platform",
+            context.platform,
+            "--framework",
+            framework_path,
+            testbed_dir,
+        ])
 
         run(
             [
@@ -840,7 +831,7 @@ def apple_sim_host(platform_name: str) -> str:
     """Determine the native simulator target for this platform."""
     for _, slice_parts in HOSTS[platform_name].items():
         for host_triple in slice_parts:
-            parts = host_triple.split('-')
+            parts = host_triple.split("-")
             if parts[0] == platform.machine() and parts[-1] == "simulator":
                 return host_triple
 
@@ -968,20 +959,29 @@ def parse_args() -> argparse.Namespace:
         cmd.add_argument(
             "--simulator",
             help=(
-                "The name of the simulator to use (eg: 'iPhone 16e'). Defaults to "
-                "the most recently released 'entry level' iPhone device. Device "
-                "architecture and OS version can also be specified; e.g., "
-                "`--simulator 'iPhone 16 Pro,arch=arm64,OS=26.0'` would run on "
-                "an ARM64 iPhone 16 Pro simulator running iOS 26.0."
+                "The name of the simulator to use (eg: 'iPhone 16e'). "
+                "Defaults to the most recently released 'entry level' "
+                "iPhone device. Device architecture and OS version can also "
+                "be specified; e.g., "
+                "`--simulator 'iPhone 16 Pro,arch=arm64,OS=26.0'` would "
+                "run on an ARM64 iPhone 16 Pro simulator running iOS 26.0."
             ),
         )
         group = cmd.add_mutually_exclusive_group()
         group.add_argument(
-            "--fast-ci", action="store_const", dest="ci_mode", const="fast",
-            help="Add test arguments for GitHub Actions")
+            "--fast-ci",
+            action="store_const",
+            dest="ci_mode",
+            const="fast",
+            help="Add test arguments for GitHub Actions",
+        )
         group.add_argument(
-            "--slow-ci", action="store_const", dest="ci_mode", const="slow",
-            help="Add test arguments for buildbots")
+            "--slow-ci",
+            action="store_const",
+            dest="ci_mode",
+            const="slow",
+            help="Add test arguments for buildbots",
+        )
 
     for subcommand in [configure_build, configure_host, build, ci]:
         subcommand.add_argument(
index 49974cb142853ca6e2c20554e8617bb2d252e9f3..0dd77ab8b827974ea5b8ebd3c48ee47f9538c612 100644 (file)
@@ -32,15 +32,15 @@ def select_simulator_device(platform):
     json_data = json.loads(raw_json)
 
     if platform == "iOS":
-        # Any iOS device will do; we'll look for "SE" devices - but the name isn't
-        # consistent over time. Older Xcode versions will use "iPhone SE (Nth
-        # generation)"; As of 2025, they've started using "iPhone 16e".
+        # Any iOS device will do; we'll look for "SE" devices - but the name
+        # isn't consistent over time. Older Xcode versions will use "iPhone SE
+        # (Nth generation)"; As of 2025, they've started using "iPhone 16e".
         #
-        # When Xcode is updated after a new release, new devices will be available
-        # and old ones will be dropped from the set available on the latest iOS
-        # version. Select the one with the highest minimum runtime version - this
-        # is an indicator of the "newest" released device, which should always be
-        # supported on the "most recent" iOS version.
+        # When Xcode is updated after a new release, new devices will be
+        # available and old ones will be dropped from the set available on the
+        # latest iOS version. Select the one with the highest minimum runtime
+        # version - this is an indicator of the "newest" released device, which
+        # should always be supported on the "most recent" iOS version.
         se_simulators = sorted(
             (devicetype["minRuntimeVersion"], devicetype["name"])
             for devicetype in json_data["devicetypes"]
@@ -295,7 +295,8 @@ def main():
 
     parser = argparse.ArgumentParser(
         description=(
-            "Manages the process of testing an Apple Python project through Xcode."
+            "Manages the process of testing an Apple Python project "
+            "through Xcode."
         ),
     )
 
@@ -336,7 +337,10 @@ def main():
 
     run = subcommands.add_parser(
         "run",
-        usage="%(prog)s [-h] [--simulator SIMULATOR] -- <test arg> [<test arg> ...]",
+        usage=(
+            "%(prog)s [-h] [--simulator SIMULATOR] -- "
+            "<test arg> [<test arg> ...]"
+        ),
         description=(
             "Run a testbed project. The arguments provided after `--` will be "
             "passed to the running iOS process as if they were arguments to "
@@ -397,9 +401,9 @@ def main():
                 / "bin"
             ).is_dir():
                 print(
-                    f"Testbed does not contain a compiled Python framework. Use "
-                    f"`python {sys.argv[0]} clone ...` to create a runnable "
-                    f"clone of this testbed."
+                    "Testbed does not contain a compiled Python framework. "
+                    f"Use `python {sys.argv[0]} clone ...` to create a "
+                    "runnable clone of this testbed."
                 )
                 sys.exit(20)
 
@@ -411,7 +415,8 @@ def main():
             )
         else:
             print(
-                f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)"
+                "Must specify test arguments "
+                f"(e.g., {sys.argv[0]} run -- test)"
             )
             print()
             parser.print_help(sys.stderr)