From 82c35fdb06a865c58a2ebd4f0ea5fb92a7618cb0 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 30 Apr 2026 21:34:53 -0700 Subject: [PATCH 1/2] cuda.core: prefer binding names for runtime errors Use the generated runtime error enum as the name source for known CUDA Runtime errors so error messages remain stable when the runtime name table differs from the installed bindings. Made-with: Cursor --- cuda_core/cuda/core/_utils/cuda_utils.pyx | 11 ++++++++++- cuda_core/tests/test_cuda_utils.py | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/cuda_core/cuda/core/_utils/cuda_utils.pyx b/cuda_core/cuda/core/_utils/cuda_utils.pyx index 867d066ce2c..d25dcb92293 100644 --- a/cuda_core/cuda/core/_utils/cuda_utils.pyx +++ b/cuda_core/cuda/core/_utils/cuda_utils.pyx @@ -149,6 +149,13 @@ cdef object _RUNTIME_SUCCESS = runtime.cudaError_t.cudaSuccess cdef object _NVRTC_SUCCESS = nvrtc.nvrtcResult.NVRTC_SUCCESS +def _known_runtime_error_name(error): + try: + return runtime.cudaError_t(error).name + except ValueError: + return None + + cpdef inline int _check_driver_error(cydriver.CUresult error) except?-1 nogil: if error == cydriver.CUresult.CUDA_SUCCESS: return 0 @@ -174,7 +181,9 @@ cpdef inline int _check_runtime_error(error) except?-1: name_err, name = runtime.cudaGetErrorName(error) if name_err != _RUNTIME_SUCCESS: raise CUDAError(f"UNEXPECTED ERROR CODE: {error}") - name = name.decode() + # Windows hybrid cudart can lag the generated bindings' enum table. + # Prefer the binding name for values the bindings know. + name = _known_runtime_error_name(error) or name.decode() expl = RUNTIME_CUDA_ERROR_EXPLANATIONS.get(int(error)) if expl is not None: raise CUDAError(f"{name}: {expl}") diff --git a/cuda_core/tests/test_cuda_utils.py b/cuda_core/tests/test_cuda_utils.py index be22e57998b..13633452e90 100644 --- a/cuda_core/tests/test_cuda_utils.py +++ b/cuda_core/tests/test_cuda_utils.py @@ -179,6 +179,22 @@ def test_check_runtime_error_attaches_explanation(): assert str(e.value) != f"{name.decode()}: {desc.decode()}" +def test_check_runtime_error_uses_binding_name_for_known_runtime_error(monkeypatch): + error = runtime.cudaError_t.cudaErrorInvalidValue + runtime_name = b"runtime-provided-name" + + def cuda_get_error_name(_error): + return runtime.cudaError_t.cudaSuccess, runtime_name + + monkeypatch.setattr(cuda_utils.runtime, "cudaGetErrorName", cuda_get_error_name) + + with pytest.raises(cuda_utils.CUDAError) as e: + cuda_utils._check_runtime_error(error) + + assert str(e.value).startswith(f"{error.name}: ") + assert runtime_name.decode() not in str(e.value) + + def test_precondition(): def checker(*args, what=""): if args[0] < 0: From 503bfacfa22adf2bbdd58315e784cfc87c7ba962 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 1 May 2026 00:11:40 -0700 Subject: [PATCH 2/2] cuda.core: simplify runtime error naming path `_check_error()` only routes `runtime.cudaError_t` instances into `_check_runtime_error()`, so consulting `cudaGetErrorName()` and keeping a fallback for unknown values does not improve the normal `cuda.core` path. The Windows hybrid cudart issue is that the runtime name table can lag the generated enum table, so using `error.name` directly is both simpler and a better match for the values the code already has. With the runtime path now relying on enum members, the runtime-side tests no longer need to account for `UNEXPECTED ERROR CODE` in this loop or keep a separate monkeypatch test for avoiding the runtime name lookup. Made-with: Cursor --- cuda_core/cuda/core/_utils/cuda_utils.pyx | 16 +++----------- cuda_core/tests/test_cuda_utils.py | 26 +---------------------- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/cuda_core/cuda/core/_utils/cuda_utils.pyx b/cuda_core/cuda/core/_utils/cuda_utils.pyx index d25dcb92293..1bcfa524884 100644 --- a/cuda_core/cuda/core/_utils/cuda_utils.pyx +++ b/cuda_core/cuda/core/_utils/cuda_utils.pyx @@ -149,13 +149,6 @@ cdef object _RUNTIME_SUCCESS = runtime.cudaError_t.cudaSuccess cdef object _NVRTC_SUCCESS = nvrtc.nvrtcResult.NVRTC_SUCCESS -def _known_runtime_error_name(error): - try: - return runtime.cudaError_t(error).name - except ValueError: - return None - - cpdef inline int _check_driver_error(cydriver.CUresult error) except?-1 nogil: if error == cydriver.CUresult.CUDA_SUCCESS: return 0 @@ -178,12 +171,9 @@ cpdef inline int _check_driver_error(cydriver.CUresult error) except?-1 nogil: cpdef inline int _check_runtime_error(error) except?-1: if error == _RUNTIME_SUCCESS: return 0 - name_err, name = runtime.cudaGetErrorName(error) - if name_err != _RUNTIME_SUCCESS: - raise CUDAError(f"UNEXPECTED ERROR CODE: {error}") - # Windows hybrid cudart can lag the generated bindings' enum table. - # Prefer the binding name for values the bindings know. - name = _known_runtime_error_name(error) or name.decode() + # `_check_error()` reaches this path only for `runtime.cudaError_t` values. + # Use the enum name directly because Windows hybrid cudart can lag that table. + name = error.name expl = RUNTIME_CUDA_ERROR_EXPLANATIONS.get(int(error)) if expl is not None: raise CUDAError(f"{name}: {expl}") diff --git a/cuda_core/tests/test_cuda_utils.py b/cuda_core/tests/test_cuda_utils.py index 13633452e90..32ea504248d 100644 --- a/cuda_core/tests/test_cuda_utils.py +++ b/cuda_core/tests/test_cuda_utils.py @@ -48,7 +48,6 @@ def test_check_driver_error(): def test_check_runtime_error(): - num_unexpected = 0 for error in runtime.cudaError_t: if error == runtime.cudaError_t.cudaSuccess: assert cuda_utils._check_runtime_error(error) == 0 @@ -56,14 +55,7 @@ def test_check_runtime_error(): with pytest.raises(cuda_utils.CUDAError) as e: cuda_utils._check_runtime_error(error) msg = str(e) - if "UNEXPECTED ERROR CODE" in msg: - num_unexpected += 1 - else: - # Example repr(error): - enum_name = repr(error).split(".", 1)[1].split(":", 1)[0] - assert enum_name in msg - # Smoke test: We don't want most to be unexpected. - assert num_unexpected < len(driver.CUresult) * 0.5 + assert error.name in msg def test_driver_error_enum_has_non_empty_docstring(): @@ -179,22 +171,6 @@ def test_check_runtime_error_attaches_explanation(): assert str(e.value) != f"{name.decode()}: {desc.decode()}" -def test_check_runtime_error_uses_binding_name_for_known_runtime_error(monkeypatch): - error = runtime.cudaError_t.cudaErrorInvalidValue - runtime_name = b"runtime-provided-name" - - def cuda_get_error_name(_error): - return runtime.cudaError_t.cudaSuccess, runtime_name - - monkeypatch.setattr(cuda_utils.runtime, "cudaGetErrorName", cuda_get_error_name) - - with pytest.raises(cuda_utils.CUDAError) as e: - cuda_utils._check_runtime_error(error) - - assert str(e.value).startswith(f"{error.name}: ") - assert runtime_name.decode() not in str(e.value) - - def test_precondition(): def checker(*args, what=""): if args[0] < 0: