diff --git a/cuda_bindings_12/cuda/bindings/_internal/utils.pyx b/cuda_bindings_12/cuda/bindings/_internal/utils.pyx index df17a9e47df..ce9012113df 100644 --- a/cuda_bindings_12/cuda/bindings/_internal/utils.pyx +++ b/cuda_bindings_12/cuda/bindings/_internal/utils.pyx @@ -98,7 +98,14 @@ cdef int get_nested_resource_ptr(nested_resource[ResT] &in_out_ptr, object obj, nested_ptr.reset(nested_vec, True) for i, obj_i in enumerate(obj): if ResT is char: - obj_i_bytes = ((obj_i)).encode() + obj_i_type = type(obj_i) + if obj_i_type is str: + obj_i_bytes = obj_i.encode("utf-8") + elif obj_i_type is bytes: + obj_i_bytes = obj_i + else: + raise TypeError( + f"Expected str or bytes, got {obj_i_type.__name__}") str_len = (len(obj_i_bytes)) + 1 # including null termination deref(nested_res_vec)[i].resize(str_len) obj_i_ptr = (obj_i_bytes) diff --git a/cuda_bindings_12/cuda/bindings/_lib/utils.pxd b/cuda_bindings_12/cuda/bindings/_lib/utils.pxd index 706c6d3fc7a..c36ddfb0d12 100644 --- a/cuda_bindings_12/cuda/bindings/_lib/utils.pxd +++ b/cuda_bindings_12/cuda/bindings/_lib/utils.pxd @@ -152,6 +152,7 @@ cdef class _HelperCUcoredumpSettings: cdef cydriver.CUcoredumpSettings_enum _attrib cdef bint _is_getter cdef size_t _size + cdef object _references # keeps caller bytes alive so _charstar stays valid # Return values cdef cpp_bool _bool diff --git a/cuda_bindings_12/cuda/bindings/_lib/utils.pxi b/cuda_bindings_12/cuda/bindings/_lib/utils.pxi index c11d0d0c197..eebdd17b41e 100644 --- a/cuda_bindings_12/cuda/bindings/_lib/utils.pxi +++ b/cuda_bindings_12/cuda/bindings/_lib/utils.pxi @@ -734,6 +734,8 @@ cdef class _HelperCUcoredumpSettings: self._cptr = self._charstar self._size = 1024 else: + # Keep a reference so the borrowed _charstar buffer stays alive. + self._references = init_value self._charstar = init_value self._cptr = self._charstar self._size = len(init_value) @@ -756,7 +758,9 @@ cdef class _HelperCUcoredumpSettings: raise TypeError('Unsupported attribute: {}'.format(attr.name)) def __dealloc__(self): - pass + # Only the getter owns heap storage; the setter borrows caller bytes. + if self._is_getter: + free(self._charstar) @property def cptr(self): diff --git a/cuda_bindings_12/cuda/bindings/utils/__init__.py b/cuda_bindings_12/cuda/bindings/utils/__init__.py index 62d083dc7c6..d6136da14b0 100644 --- a/cuda_bindings_12/cuda/bindings/utils/__init__.py +++ b/cuda_bindings_12/cuda/bindings/utils/__init__.py @@ -26,6 +26,9 @@ def get_cuda_native_handle(obj: Any) -> int: """ obj_type = type(obj) try: - return _handle_getters[obj_type](obj) + getter = _handle_getters[obj_type] except KeyError: raise TypeError("Unknown type: " + str(obj_type)) from None + # Deliberately outside the try: a KeyError raised by the getter itself is a + # bug in that getter, not an unregistered type. + return getter(obj) diff --git a/cuda_bindings_12/tests/test_nvjitlink.py b/cuda_bindings_12/tests/test_nvjitlink.py index 19dc040e47d..bec58b7a048 100644 --- a/cuda_bindings_12/tests/test_nvjitlink.py +++ b/cuda_bindings_12/tests/test_nvjitlink.py @@ -100,6 +100,13 @@ def test_create_and_destroy(option): nvjitlink.destroy(handle) +@pytest.mark.human_authored +def test_create_and_destroy_bytes_options(): + handle = nvjitlink.create(1, [b"-arch=sm_80"]) + assert handle != 0 + nvjitlink.destroy(handle) + + @pytest.mark.parametrize("option", ARCHITECTURES) def test_complete_empty(option): handle = nvjitlink.create(1, [f"-arch={option}"]) diff --git a/cuda_bindings_12/tests/test_nvvm.py b/cuda_bindings_12/tests/test_nvvm.py index bd6f49f03f1..3cea5a0f817 100644 --- a/cuda_bindings_12/tests/test_nvvm.py +++ b/cuda_bindings_12/tests/test_nvvm.py @@ -246,7 +246,7 @@ def test_get_buffer_empty(get_size, get_buffer): assert buffer == b"\x00" -@pytest.mark.parametrize("options", [[], ["-opt=0"], ["-opt=3", "-g"]]) +@pytest.mark.parametrize("options", [[], ["-opt=0"], ["-opt=3", "-g"], [b"-opt=0"]]) def test_compile_program_with_minimal_nvvm_ir(minimal_nvvmir, options): with nvvm_program() as prog: nvvm.add_module_to_program(prog, minimal_nvvmir, len(minimal_nvvmir), "FileNameHere.ll") diff --git a/cuda_bindings_12/tests/test_utils.py b/cuda_bindings_12/tests/test_utils.py index 8904d65d2ed..33487c7f529 100644 --- a/cuda_bindings_12/tests/test_utils.py +++ b/cuda_bindings_12/tests/test_utils.py @@ -92,6 +92,27 @@ def test_get_handle_error(target): handle = get_cuda_native_handle(target) +@pytest.mark.agent_authored(model="claude-opus-5") +def test_get_handle_does_not_report_a_registered_type_as_unknown(monkeypatch): + """A KeyError from inside a handle getter is a bug in that getter. + + Reporting it as "Unknown type" is wrong twice over: the type *is* + registered, and `from None` hides the traceback that would say otherwise. + """ + from cuda.bindings.utils import _handle_getters + + class Registered: + pass + + def getter(_obj): + raise KeyError("lookup inside the getter failed") + + monkeypatch.setitem(_handle_getters, Registered, getter) + + with pytest.raises(KeyError, match="lookup inside the getter failed"): + get_cuda_native_handle(Registered()) + + @pytest.mark.parametrize( "module", [