Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cuda_core/cuda/core/_utils/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ def check_str_enum(value, enum_class, *, allow_none=False):
"""
if allow_none and value is None:
return
if value not in {m.value for m in enum_class}:
# Membership is tested against a tuple rather than a set on purpose:
# ``x in {...}`` hashes ``x``, so an unhashable argument (a list, a dict,
# a bytearray) would raise ``TypeError: unhashable type`` from inside the
# check instead of the ValueError this function exists to raise.
if value not in tuple(m.value for m in enum_class):
valid = sorted(m.value for m in enum_class)
if allow_none:
valid = [None, *valid]
Expand Down
6 changes: 6 additions & 0 deletions cuda_core/docs/source/release/1.2.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ Fixes and enhancements
Windows, both ``ctypes.CFUNCTYPE`` and ``ctypes.WINFUNCTYPE`` are accepted.
(`#2439 <https://github.com/NVIDIA/cuda-python/issues/2439>`__)

- String-enum options now report an unhashable argument the same way as any
other invalid value. Passing a ``list`` or ``dict`` where a string enum was
expected -- for example ``WorkqueueResourceOptions(sharing_scope=[...])`` --
raised ``TypeError: unhashable type`` from inside the validator instead of
the documented ``ValueError`` naming the accepted values.

Deprecation Notices
-------------------

Expand Down
67 changes: 67 additions & 0 deletions cuda_core/tests/test_utils_validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

import pytest

from cuda.core import WorkqueueResourceOptions
from cuda.core._utils.validators import check_str_enum, format_or_list
from cuda.core.typing import SourceCodeType


@pytest.mark.agent_authored(model="claude-opus-5")
@pytest.mark.parametrize(
("values", "expected"),
[
pytest.param([], "", id="empty"),
pytest.param(["a"], "'a'", id="one"),
pytest.param(["a", "b"], "'a' or 'b'", id="two"),
pytest.param(["a", "b", "c"], "'a', 'b' or 'c'", id="three"),
pytest.param([None, "a"], "None or 'a'", id="none-first"),
],
)
def test_format_or_list(values, expected):
assert format_or_list(values) == expected


@pytest.mark.agent_authored(model="claude-opus-5")
@pytest.mark.parametrize("value", ["c++", "ptx", "nvvm", SourceCodeType.CXX])
def test_check_str_enum_accepts_members_and_their_values(value):
check_str_enum(value, SourceCodeType)


@pytest.mark.agent_authored(model="claude-opus-5")
def test_check_str_enum_allow_none():
check_str_enum(None, SourceCodeType, allow_none=True)

with pytest.raises(ValueError, match="None is not a valid SourceCodeType"):
check_str_enum(None, SourceCodeType)


@pytest.mark.agent_authored(model="claude-opus-5")
@pytest.mark.parametrize(
"value",
[
pytest.param("fortran", id="str"),
pytest.param(5, id="int"),
# Unhashable arguments used to blow up inside the membership test with
# "TypeError: unhashable type" before the ValueError was ever built.
pytest.param(["c++"], id="list"),
pytest.param({"code_type": "c++"}, id="dict"),
pytest.param(bytearray(b"c++"), id="bytearray"),
],
)
def test_check_str_enum_rejects_invalid_values_with_value_error(value):
with pytest.raises(ValueError, match="is not a valid SourceCodeType. Must be "):
check_str_enum(value, SourceCodeType)


@pytest.mark.agent_authored(model="claude-opus-5")
def test_options_reject_an_unhashable_scope_with_value_error():
"""A public entry point must report a bad option the same way for every
kind of bad value. ``WorkqueueResourceOptions.__post_init__`` validates
``sharing_scope`` through ``check_str_enum``, so an unhashable argument
used to surface as ``TypeError: unhashable type: 'list'`` while a plain
string got the documented ValueError."""
with pytest.raises(ValueError, match="is not a valid WorkqueueSharingScopeType. Must be "):
WorkqueueResourceOptions(sharing_scope=["green_ctx_balanced"])
Loading