Release: dev → main (first public release) #258

Merged
bvandeusen merged 94 commits from dev into main 2026-09-25 10:02:40 -04:00
2 changed files with 58 additions and 7 deletions
Showing only changes of commit 42a40d71a4 - Show all commits
+23 -2
View File
@@ -69,11 +69,32 @@ def onnx_status(imp=importlib.import_module, load=ctypes.CDLL) -> dict:
except OSError as e:
out["device"] = "cpu"
out["error"] = str(e)
else:
out["device"] = "cuda"
return out
# Loading proves the libraries resolve, NOT that a GPU can be used: on
# 2026-09-24 this reported "onnx on GPU" beside torch failing cuInit with
# "CUDA unknown error" (a driver update awaiting a reboot). Asking the CUDA
# runtime for a device initialises the driver the provider would use.
error = _cuda_device_error(load)
out["device"] = "cpu" if error else "cuda"
if error:
out["error"] = error
return out
def _cuda_device_error(load=ctypes.CDLL) -> str | None:
"""None when the CUDA runtime can reach a device, else why it cannot."""
try:
cudart = load("libcudart.so.13")
except OSError as e:
return str(e)
count = ctypes.c_int(0)
rc = cudart.cudaGetDeviceCount(ctypes.byref(count))
if rc != 0:
cudart.cudaGetErrorString.restype = ctypes.c_char_p
return f"cudaGetDeviceCount: {cudart.cudaGetErrorString(rc).decode()} ({rc})"
return None if count.value > 0 else "no CUDA device visible"
def report() -> dict:
"""Check both runtimes, log the result, and keep it for /status."""
LAST.clear()
+35 -5
View File
@@ -28,15 +28,45 @@ def _imp(mod):
return lambda name: mod
def test_onnx_on_gpu_when_the_cuda_provider_loads():
class _Cudart:
"""libcudart as ctypes sees it: cudaGetDeviceCount writes through a pointer."""
def __init__(self, rc=0, count=1):
self.rc = rc
self.count = count
self.cudaGetErrorString = lambda rc: b"unknown error"
def cudaGetDeviceCount(self, ref):
ref._obj.value = self.count
return self.rc
def _loader(calls, cudart):
def load(path, mode=0):
calls.append(path)
return cudart if path == "libcudart.so.13" else None
return load
GPU_BUILD = ["CUDAExecutionProvider", "CPUExecutionProvider"]
def test_onnx_on_gpu_when_the_provider_loads_and_a_device_answers():
calls = []
s = accel.onnx_status(
_imp(_ort(["CUDAExecutionProvider", "CPUExecutionProvider"], lambda: calls.append("preload"))),
load=lambda path, mode=0: calls.append(path),
_imp(_ort(GPU_BUILD, lambda: calls.append("preload"))),
load=_loader(calls, _Cudart()),
)
assert s["device"] == "cuda"
assert calls[0] == "preload"
assert calls[-1].endswith("capi/libonnxruntime_providers_cuda.so")
assert any(c.endswith("capi/libonnxruntime_providers_cuda.so") for c in calls)
def test_onnx_libraries_loading_is_not_a_gpu_when_cuda_cannot_initialise():
"""The 2026-09-24 case: every library resolved, cuInit failed."""
s = accel.onnx_status(_imp(_ort(GPU_BUILD)), load=_loader([], _Cudart(rc=999)))
assert s["device"] == "cpu"
assert "unknown error" in s["error"]
def test_onnx_listed_but_unloadable_reports_cpu_with_the_reason():
@@ -44,7 +74,7 @@ def test_onnx_listed_but_unloadable_reports_cpu_with_the_reason():
if path.endswith("providers_cuda.so"):
raise OSError("libcudart.so.13: cannot open shared object file")
s = accel.onnx_status(_imp(_ort(["CUDAExecutionProvider", "CPUExecutionProvider"])), load=load)
s = accel.onnx_status(_imp(_ort(GPU_BUILD)), load=load)
assert s["device"] == "cpu"
assert "libcudart.so.13" in s["error"]