2026-07-30 14:45:21 +02:00

82 lines
2.7 KiB
Python

"""Device (CPU/GPU) identity.
Each runner self-reports its hardware in the result `env` (cpu, arch, device).
The harness derives a device record from that, so identity is correct even for a
remote runner or a future GPU adapter (which would report device="gpu"). A CLI
`--device-label` overrides only the human label, e.g. to disambiguate two machines
that report the same CPU model string.
"""
from __future__ import annotations
import platform
import re
from typing import Any, Optional
def slug(s: str) -> str:
out = re.sub(r"[^a-zA-Z0-9]+", "-", s.strip().lower()).strip("-")
return out or "unknown"
def device_from_env(env: dict[str, Any], override_label: Optional[str] = None) -> dict[str, str]:
"""Build a device record from a runner's reported env, falling back to the
host when the runner did not report hardware (older/minimal adapters).
The auto label combines the device model with the OS version so runs on the
same CPU under different OS/kernel versions get distinct labels."""
name = env.get("cpu") or _host_cpu()
arch = env.get("arch") or platform.machine() or "unknown"
dtype = env.get("device") or "cpu"
os_name = env.get("os") or platform.system().lower() or "unknown"
os_version = env.get("os_version") or platform.release() or "unknown"
return {
"type": dtype,
"name": name,
"arch": arch,
"os": os_name,
"os_version": os_version,
"label": override_label or slug(f"{name}-{os_version}"),
}
# Field priority works across arches: "model name" (x86), "Model" (Raspberry Pi
# board), "Hardware" (older ARM), "cpu model" (others).
_CPU_FIELDS = ("model name", "Model", "Hardware", "cpu model")
def parse_cpu_model(cpuinfo_text: str) -> str:
found: dict[str, str] = {}
for line in cpuinfo_text.splitlines():
if ":" not in line:
continue
k, _, v = line.partition(":")
k, v = k.strip(), v.strip()
if k in _CPU_FIELDS and k not in found and v:
found[k] = v
for f in _CPU_FIELDS:
if f in found:
return found[f]
return "unknown"
def _host_cpu() -> str:
try:
model = parse_cpu_model(open("/proc/cpuinfo").read())
if model != "unknown":
return model
except OSError:
pass
if platform.system() == "Darwin": # macOS has no /proc
try:
import subprocess
out = subprocess.run(
["sysctl", "-n", "machdep.cpu.brand_string"],
capture_output=True, text=True, timeout=2,
)
if out.returncode == 0 and out.stdout.strip():
return out.stdout.strip()
except Exception: # noqa: BLE001
pass
return platform.processor() or "unknown"