From 7a1ff278d2b9337f645416eb7cd89fc40e517ecd Mon Sep 17 00:00:00 2001 From: LeSingh1 Date: Sun, 9 Aug 2026 14:15:36 -0700 Subject: [PATCH] fix(core): accept the documented str symbol_mapping in ObjectCode.get_kernel Every `ObjectCode.from_*` constructor annotates and documents `symbol_mapping: dict[str, str] | None`. `get_kernel` encodes the name only on the mapping MISS path: try: name = self._sym_map[name] except KeyError: if isinstance(name, str): name = name.encode() cdef KernelHandle h_kernel = create_kernel_handle(self._h_library, name) On a HIT, `name` is rebound to the mapped value and handed straight to ``. cuda_core sets no `c_string_type`/`c_string_encoding` directive (build_hooks.py passes only embedsignature, warn.deprecated.IF and freethreading_compatible), so Cython's default applies and a `str` raises "TypeError: expected bytes, str found". So the documented form fails for exactly the names it is supposed to translate: ObjectCode.from_cubin(cubin, symbol_mapping={"saxpy": mangled_str}) .get_kernel("saxpy") # TypeError .get_kernel("not_in_the_map") # fine The reason this has gone unnoticed: `Program.compile` fills the mapping from `nvrtcGetLoweredName`, whose `const char*` Cython converts to `bytes`, and every existing test round-trips `mod.symbol_mapping` straight back into a `from_*` constructor -- so only bytes values are ever exercised. Move the encode past the lookup so both value types work. Compile-produced mappings are unaffected. --- cuda_core/cuda/core/_module.pyx | 12 +++++++++-- cuda_core/docs/source/release/1.2.0-notes.rst | 7 +++++++ cuda_core/tests/test_module.py | 21 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/cuda_core/cuda/core/_module.pyx b/cuda_core/cuda/core/_module.pyx index 95e149065bf..60e4af3db52 100644 --- a/cuda_core/cuda/core/_module.pyx +++ b/cuda_core/cuda/core/_module.pyx @@ -802,8 +802,16 @@ cdef class ObjectCode: try: name = self._sym_map[name] except KeyError: - if isinstance(name, str): - name = name.encode() + pass + # Encode after the lookup, not only on the miss path. symbol_mapping is + # annotated and documented `dict[str, str]`, but a str mapped value used + # to reach `` unencoded and raise + # "TypeError: expected bytes, str found". Program.compile stores the + # lowered names as bytes (nvrtcGetLoweredName), so only the documented + # hand-built form was affected -- i.e. the mapping worked only when it + # did nothing. + if isinstance(name, str): + name = name.encode() cdef KernelHandle h_kernel = create_kernel_handle(self._h_library, name) if not h_kernel: diff --git a/cuda_core/docs/source/release/1.2.0-notes.rst b/cuda_core/docs/source/release/1.2.0-notes.rst index 120d2c2a253..fc028da9334 100644 --- a/cuda_core/docs/source/release/1.2.0-notes.rst +++ b/cuda_core/docs/source/release/1.2.0-notes.rst @@ -73,6 +73,13 @@ Fixes and enhancements Windows, both ``ctypes.CFUNCTYPE`` and ``ctypes.WINFUNCTYPE`` are accepted. (`#2439 `__) +- :meth:`ObjectCode.get_kernel` now accepts a ``symbol_mapping`` whose values + are ``str``, as :meth:`ObjectCode.from_cubin` and its siblings document + (``dict[str, str]``). The mapped name was only encoded on the *miss* path, so + a hand-built mapping raised ``TypeError: expected bytes, str found`` for + exactly the names it was supposed to translate. Mappings produced by + :meth:`Program.compile` are unaffected -- their values are already ``bytes``. + Deprecation Notices ------------------- diff --git a/cuda_core/tests/test_module.py b/cuda_core/tests/test_module.py index 25cf0e24de4..cb30447823e 100644 --- a/cuda_core/tests/test_module.py +++ b/cuda_core/tests/test_module.py @@ -369,6 +369,27 @@ def test_object_code_load_cubin(get_saxpy_kernel_cubin): mod.get_kernel("saxpy") # force loading +@pytest.mark.agent_authored(model="claude-opus-5") +def test_object_code_symbol_mapping_accepts_str_values(get_saxpy_kernel_cubin): + """``symbol_mapping`` is annotated and documented ``dict[str, str]``. + + ``Program.compile`` stores the lowered names as ``bytes`` (they come from + ``nvrtcGetLoweredName``), so every existing test round-trips + ``mod.symbol_mapping`` unchanged and the documented ``str`` form is never + exercised. On a mapping *hit* the value went straight to ```` + without being encoded, so a hand-built ``dict[str, str]`` raised + ``TypeError: expected bytes, str found`` -- the mapping worked only for + names it did not map. + """ + _, mod = get_saxpy_kernel_cubin + cubin = mod.code + str_sym_map = {k: v.decode() if isinstance(v, bytes) else v for k, v in mod.symbol_mapping.items()} + assert all(isinstance(v, str) for v in str_sym_map.values()) + + obj = ObjectCode.from_cubin(cubin, symbol_mapping=str_sym_map) + obj.get_kernel("saxpy") # force loading through the mapped name + + def test_object_code_load_cubin_from_file(get_saxpy_kernel_cubin, tmp_path, convert_path): _, mod = get_saxpy_kernel_cubin cubin = mod.code