From 47bf44289497291d4375ca90cccd9a89737a2922 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Fri, 17 Jul 2026 18:58:23 -0700 Subject: [PATCH] fix(hardware): detect Apple Silicon under Rosetta 2 in GPU probe (BE-3435) An x86_64 Python running under Rosetta 2 on an Apple Silicon Mac reports platform.machine() == 'x86_64', so _detect_gpu skipped the unified-memory block and fell through to the NVIDIA/AMD probes, reporting gpu: null on a capable machine. Add _is_apple_silicon(), which keeps the native arm64 fast path and falls back to sysctl.proc_translated == '1' (via the existing _run helper) to recognize the translated case. Genuine Intel Macs lack that sysctl key, so _run returns None and they still fall through unchanged. Preserves the never-raise contract (_run never raises; the whole probe stays wrapped). Adds a unit test mocking _run for the Rosetta case. --- comfy_cli/hardware.py | 17 ++++++++++++++++- tests/comfy_cli/test_hardware.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/comfy_cli/hardware.py b/comfy_cli/hardware.py index 61aba2b3..c5086c22 100644 --- a/comfy_cli/hardware.py +++ b/comfy_cli/hardware.py @@ -227,6 +227,21 @@ def _detect_gpu_amd() -> dict | None: } +def _is_apple_silicon(machine: str) -> bool: + """True when the underlying CPU is Apple Silicon (Darwin already assumed). + + A native arm64 process reports ``machine == 'arm64'`` directly. An x86_64 + Python running under Rosetta 2 on an Apple Silicon Mac reports + ``machine == 'x86_64'``, so fall back to ``sysctl.proc_translated`` — a value + of ``'1'`` means the process is translated, which only happens on Apple + Silicon. Genuine Intel Macs lack that sysctl key, so ``_run`` returns + ``None`` and this stays False. + """ + if machine == "arm64": + return True + return _run(["sysctl", "-n", "sysctl.proc_translated"]) == "1" + + def _detect_gpu(system: str, machine: str, cpu: str | None) -> dict | None: """Resolve the GPU block, or ``None`` if no GPU is detected. @@ -234,7 +249,7 @@ def _detect_gpu(system: str, machine: str, cpu: str | None) -> dict | None: other path tries NVIDIA (nvidia-smi then ctypes), then AMD. """ try: - if system == "Darwin" and machine == "arm64": + if system == "Darwin" and _is_apple_silicon(machine): return { "vendor": "apple", "model": cpu, diff --git a/tests/comfy_cli/test_hardware.py b/tests/comfy_cli/test_hardware.py index c496f990..1bc7e65b 100644 --- a/tests/comfy_cli/test_hardware.py +++ b/tests/comfy_cli/test_hardware.py @@ -65,6 +65,36 @@ def test_intel_mac_has_no_apple_gpu(self): assert hw["gpu"] is None assert hw["ram_bytes"] == 17179869184 + def test_apple_silicon_under_rosetta_unified_block(self): + """An x86_64 Python under Rosetta 2 reports machine == 'x86_64', but + sysctl.proc_translated == '1' reveals the underlying Apple Silicon, so + the unified-memory block is still reported (not gpu null).""" + + def fake_run(cmd): + if cmd == ["sysctl", "-n", "sysctl.proc_translated"]: + return "1" + if cmd == ["sysctl", "-n", "machdep.cpu.brand_string"]: + return "Apple M4 Max" + return None + + with ( + patch.object(hardware.platform, "system", return_value="Darwin"), + patch.object(hardware.platform, "machine", return_value="x86_64"), + patch.object(hardware.platform, "release", return_value="25.4.0"), + patch.object(hardware, "_run", side_effect=fake_run), + patch.object(hardware, "_detect_ram_bytes", return_value=68719476736), + ): + hw = hardware.detect_hardware() + + assert hw["arch"] == "x86_64" + assert hw["cpu"] == "Apple M4 Max" + assert hw["gpu"] == { + "vendor": "apple", + "model": "Apple M4 Max", + "vram_bytes": None, + "unified_memory": True, + } + class TestDetectHardwareNvidiaSmi: def test_nvidia_smi_happy_path(self):