Skip to content
Merged
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
3 changes: 3 additions & 0 deletions cuda_bindings_12/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ ignore = [
exclude = ["cuda/bindings/_version.py"]

[tool.ruff.lint.per-file-ignores]
# This config replaces ../ruff.toml for this tree, so it repeats that file's
# S101 policy: asserts are rejected only in cuda_core/cuda/core.
"**" = ["S101"]
"setup.py" = ["F401"]
"__init__.py" = ["F401"]
"site-packages/_cuda_bindings_redirector.py" = ["F401"]
Expand Down
6 changes: 5 additions & 1 deletion cuda_core/cuda/core/_device.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1684,7 +1684,11 @@ cdef inline int Device_resolve_device_id(device_id) except? -1:
elif err == cydriver.CUresult.CUDA_ERROR_INVALID_CONTEXT:
with nogil:
HANDLE_RETURN(cydriver.cuCtxGetCurrent(&ctx))
assert <void*>(ctx) == NULL
if <void*>(ctx) != NULL:
raise RuntimeError(
"internal cuda.core error, please report: cuCtxGetDevice returned "
"CUDA_ERROR_INVALID_CONTEXT while a context is current"
)
return 0 # cudart behavior
else:
HANDLE_RETURN(err)
Expand Down
5 changes: 4 additions & 1 deletion cuda_core/cuda/core/_layout.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1182,7 +1182,10 @@ cdef inline int unsqueeze_extents(BaseLayout& out_layout, BaseLayout& in_layout,
out_layout.shape[i] = in_shape[in_i]
out_layout.strides[i] = in_strides[in_i]
in_i += 1
assert in_i == ndim
if in_i != ndim:
raise RuntimeError(
f"internal cuda.core error, please report: unsqueeze_extents consumed {in_i} of {ndim} input axes"
)
return 0


Expand Down
3 changes: 2 additions & 1 deletion cuda_core/cuda/core/_memory/_managed_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@ def accessed_by(self, locations: Iterable[Device | Host]) -> None:
target.add(loc)
for loc in target:
spec = _coerce_location(loc)
assert spec is not None
if spec is None:
raise RuntimeError(f"internal cuda.core error, please report: no location spec for {loc!r}")
if spec.kind not in ("device", "host"):
raise ValueError(f"advise {_SET_ACCESSED_BY.name} does not support location_type='{spec.kind}'")
current = set(_query_accessed_by(self))
Expand Down
3 changes: 2 additions & 1 deletion cuda_core/cuda/core/_memory/_managed_memory_resource.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ cdef class ManagedMemoryResource(_MemPool):
(``prefetch``, ``discard``, ``discard_prefetch``).
"""
MP_check_open(self)
assert isinstance(stream, Stream), "Only Stream is supported for managed memory allocations"
if not isinstance(stream, Stream):
raise TypeError(f"Only Stream is supported for managed memory allocations, got {type(stream).__name__}")
if self.is_mapped:
raise TypeError("Cannot allocate from a mapped IPC-enabled memory resource")
cdef Stream s = Stream_accept(stream)
Expand Down
13 changes: 9 additions & 4 deletions cuda_core/cuda/core/_memoryview.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1044,14 +1044,17 @@ cdef StridedMemoryView view_as_dlpack(obj, stream_ptr, view=None):
stream_ptr = int(stream_ptr)
dldevice, device_id = obj.__dlpack_device__()
if dldevice == _kDLCPU:
assert device_id == 0
if device_id != 0:
raise ValueError(f"__dlpack_device__() reported device_id {device_id} for a kDLCPU tensor; expected 0")
device_id = -1
if stream_ptr is None:
raise BufferError("stream=None is ambiguous with view()")
elif stream_ptr == -1:
stream_ptr = None
elif dldevice == _kDLCUDA:
assert device_id >= 0
if device_id < 0:
raise ValueError(
f"__dlpack_device__() reported device_id {device_id} for a kDLCUDA tensor; expected 0 or more")
is_device_accessible = True
# no need to check other stream values, it's a pass-through
if stream_ptr is None:
Expand Down Expand Up @@ -1094,7 +1097,7 @@ cdef StridedMemoryView view_as_dlpack(obj, stream_ptr, view=None):
is_readonly = False
used_name = DLPACK_TENSOR_USED_NAME
else:
assert False
raise ValueError(f"__dlpack__() returned {capsule!r}, not an unconsumed DLPack capsule")

cpython.PyCapsule_SetName(capsule, used_name)

Expand Down Expand Up @@ -1218,7 +1221,9 @@ cpdef StridedMemoryView view_as_cai(obj, stream_ptr, view=None):
if stream is not None:
producer_s = <intptr_t>(stream)
consumer_s = <intptr_t>(stream_ptr)
assert producer_s > 0
if producer_s <= 0:
raise ValueError(
f"__cuda_array_interface__ reported stream {stream}; expected None or a positive stream handle")
# establish stream order
if producer_s != consumer_s:
with nogil:
Expand Down
3 changes: 2 additions & 1 deletion cuda_core/cuda/core/_module.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,8 @@ cdef class ObjectCode:

@classmethod
def _init(cls, module, code_type, *, name: str = "", symbol_mapping: dict[str, str] | None = None) -> ObjectCode:
assert code_type in _supported_code_type, f"{code_type=} is not supported"
if code_type not in _supported_code_type:
raise ValueError(f"{code_type=} is not supported")
cdef ObjectCode self = ObjectCode.__new__(ObjectCode)

# _h_library is assigned during _lazy_load_module
Expand Down
3 changes: 2 additions & 1 deletion cuda_core/cuda/core/_program.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1359,7 +1359,8 @@ cdef inline object _prepare_nvvm_options_impl(object opts, bint as_bytes):
options = []

# Options supported by NVVM
assert opts.arch is not None
if opts.arch is None:
raise RuntimeError("internal cuda.core error, please report: ProgramOptions.arch is None")
arch = opts.arch
if arch.startswith("sm_"):
arch = f"compute_{arch[3:]}"
Expand Down
5 changes: 2 additions & 3 deletions cuda_core/cuda/core/system/_device.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,8 @@ if TYPE_CHECKING:
cdef object _pstate_to_int(object pstate):
if pstate == nvml.Pstates.PSTATE_UNKNOWN:
return None
assert (
int(pstate) >= 0 and int(pstate) <= 15
), f"Invalid P-state: {pstate}. Must be between 0 and 15 inclusive, or PSTATE_UNKNOWN."
if int(pstate) < 0 or int(pstate) > 15:
raise ValueError(f"Invalid P-state: {pstate}. Must be between 0 and 15 inclusive, or PSTATE_UNKNOWN.")
return int(pstate) - int(nvml.Pstates.PSTATE_0)


Expand Down
3 changes: 2 additions & 1 deletion cuda_core/cuda/core/system/_field_values.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ cdef class FieldValue:
cdef object _field_value

def __init__(self, field_value: nvml.FieldValue):
assert len(field_value) == 1
if len(field_value) != 1:
raise ValueError(f"FieldValue expects exactly one field value, got {len(field_value)}")
self._field_value = field_value

@property
Expand Down
3 changes: 2 additions & 1 deletion cuda_core/cuda/core/system/_system_events.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ cdef class SystemEvent:
cdef object _event_data

def __init__(self, event_data not None: nvml.SystemEventData_v1):
assert len(event_data) == 1
if len(event_data) != 1:
raise ValueError(f"SystemEvent expects exactly one event, got {len(event_data)}")
self._event_data = event_data

@property
Expand Down
3 changes: 2 additions & 1 deletion cuda_core/cuda/core/utils/_program_cache/_file_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ def _with_sharing_retry(
last_exc = exc
if on_exhausted is not None:
return on_exhausted(last_exc)
assert last_exc is not None # at least one iteration ran and caught a PermissionError
if last_exc is None:
raise RuntimeError("internal cuda.core error, please report: sharing retry ran no attempts")
raise last_exc


Expand Down
3 changes: 2 additions & 1 deletion cuda_core/cuda/core/utils/_program_cache/_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,8 @@ def hash_version_probe(self, update: Callable[[str, bytes], None]) -> None:
# so the bytes are still in the digest via ``linker_version``.
use_driver = self._decide_driver()
if use_driver is None:
assert self._cached_decision_exc is not None
if self._cached_decision_exc is None:
raise RuntimeError("internal cuda.core error, please report: linker probe failed without an exception")
_hash_probe_failure(update, "linker", self._cached_decision_exc)
return
try:
Expand Down
19 changes: 19 additions & 0 deletions cuda_core/docs/source/release/1.3.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,25 @@ Fixes and enhancements
physical allocations.
(`#2694 <https://github.com/NVIDIA/cuda-python/issues/2694>`__)

- Checks that were ``assert`` statements now raise exceptions, so they also
run under ``python -O``, which strips asserts from Python and Cython code.
:meth:`~utils.StridedMemoryView.from_dlpack` raises ``ValueError`` when
``__dlpack_device__()`` reports a negative device ID for a CUDA tensor or a
nonzero one for a CPU tensor, or when ``__dlpack__()`` returns anything
other than an unconsumed DLPack capsule.
:meth:`~utils.StridedMemoryView.from_cuda_array_interface` raises
``ValueError`` when the producer's ``stream`` is 0 or negative.
:meth:`ManagedMemoryResource.allocate` raises ``TypeError`` when ``stream``
is not a :class:`Stream`. Unpickling an :class:`ObjectCode` with an unknown
code type raises ``ValueError``, as do :class:`~system._device.FieldValue`
and :class:`~system._system_events.SystemEvent` when the NVML data does not
hold exactly one entry, and :attr:`system.Device.performance_state` when
NVML reports a P-state outside 0 to 15. Before, these raised
``AssertionError``, or nothing under ``python -O``. A failed internal check
now raises ``RuntimeError`` with a message that starts with "internal
cuda.core error, please report".
(`#3202 <https://github.com/NVIDIA/cuda-python/issues/3202>`__)

Deprecations
------------

Expand Down
6 changes: 6 additions & 0 deletions cuda_core/tests/system/test_system_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,12 @@ def test_field_value_decodes_signed_long_long():
assert type(value) is int


@pytest.mark.agent_authored(model="claude-opus-5-5")
def test_field_value_rejects_multiple_values():
with pytest.raises(ValueError, match="exactly one field value, got 2"):
_device.FieldValue(nvml.FieldValue(2))


@pytest.mark.skipif(helpers.IS_WSL or helpers.IS_WINDOWS, reason="Device attributes not supported on WSL or Windows")
def test_get_all_devices_with_cpu_affinity(subtests):
for i in range(multiprocessing.cpu_count()):
Expand Down
6 changes: 6 additions & 0 deletions cuda_core/tests/system/test_system_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ def test_system_events_wraps_event_data():
assert event.gpu_id == 0x0000_0200


@pytest.mark.agent_authored(model="claude-opus-5-5")
def test_system_event_rejects_multiple_events():
with pytest.raises(ValueError, match="exactly one event, got 2"):
SystemEvent(nvml.SystemEventData_v1(2))


@pytest.mark.agent_authored(model="claude-opus-4.7")
@pytest.mark.parametrize(
("gpu_id", "expected"),
Expand Down
18 changes: 18 additions & 0 deletions cuda_core/tests/test_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,24 @@ def test_managed_memory_resource_buffer_dlpack_device_type(init_cuda):
assert view.__dlpack_device__() == (int(DLDeviceType.kDLCUDAManaged), 0)


@pytest.mark.agent_authored(model="claude-opus-5-5")
def test_managed_memory_resource_allocate_rejects_graph_builder(init_cuda):
device = init_cuda
skip_if_managed_memory_unsupported(device)
mr = None
gb = None
try:
mr = create_managed_memory_resource_or_skip(ManagedMemoryResourceOptions(preferred_location=device.device_id))
gb = device.create_graph_builder()
with pytest.raises(TypeError, match="Only Stream is supported .*got GraphBuilder"):
mr.allocate(64, stream=gb)
finally:
if gb is not None:
gb.close()
if mr is not None:
mr.close()


@pytest.mark.parametrize("mr_kind", ["device", "pinned"])
def test_non_managed_resources_report_not_managed(init_cuda, mr_kind):
"""Non-managed memory resources must report is_managed=False."""
Expand Down
6 changes: 6 additions & 0 deletions cuda_core/tests/test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,12 @@ def test_module_serialization_roundtrip(get_saxpy_kernel_cubin):
assert objcode.code_type == result.code_type


@pytest.mark.agent_authored(model="claude-opus-5-5")
def test_module_unpickle_rejects_unknown_code_type():
with pytest.raises(ValueError, match="code_type='bogus' is not supported"):
ObjectCode._reduce_helper(b"", "bogus", "", None)


def test_kernel_from_handle(get_saxpy_kernel_cubin):
"""Test Kernel.from_handle() with a valid handle"""
original_kernel, objcode = get_saxpy_kernel_cubin
Expand Down
10 changes: 10 additions & 0 deletions cuda_core/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,16 @@ def test_view_as_cai_device_pointer_and_stream_ordering(init_cuda):
dev.default_stream.sync()


@pytest.mark.agent_authored(model="claude-opus-5-5")
def test_view_as_cai_rejects_stream_zero(init_cuda):
"""The CUDA Array Interface does not allow ``stream`` 0."""
consumer = init_cuda.create_stream()
obj = _make_cuda_array_interface_obj(shape=(4,), strides=None, typestr="<f4", data=(0, False), stream=0)

with pytest.raises(ValueError, match="reported stream 0"):
StridedMemoryView.from_cuda_array_interface(obj, stream_ptr=consumer.handle)


def test_strided_memory_view_init_cai_path_deprecated(init_cuda):
"""The deprecated ``StridedMemoryView(obj)`` constructor routes a CAI-only
object through the CAI branch (warn + ``view_as_cai``), not the DLPack one."""
Expand Down
31 changes: 31 additions & 0 deletions cuda_core/tests/test_utils_dlpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,37 @@ def __dlpack__(self, **kwargs):
StridedMemoryView.from_dlpack(_FakeCudaDevice(), stream_ptr=None)


@pytest.mark.agent_authored(model="claude-opus-5-5")
@pytest.mark.parametrize(("device_type", "device_id"), [(DLDeviceType.kDLCPU, 1), (DLDeviceType.kDLCUDA, -1)])
def test_from_dlpack_rejects_invalid_device_id(device_type, device_id):
"""A device ordinal the DLPack device type cannot have is rejected."""

class _FakeInvalidDeviceId:
def __dlpack_device__(self):
return (int(device_type), device_id)

def __dlpack__(self, **kwargs):
raise AssertionError("__dlpack__ must not be reached")

with pytest.raises(ValueError, match=f"reported device_id {device_id}"):
StridedMemoryView.from_dlpack(_FakeInvalidDeviceId(), stream_ptr=-1)


@pytest.mark.agent_authored(model="claude-opus-5-5")
def test_from_dlpack_rejects_non_capsule():
"""``__dlpack__`` must return an unconsumed DLPack capsule."""

class _FakeNonCapsule:
def __dlpack_device__(self):
return (int(DLDeviceType.kDLCPU), 0)

def __dlpack__(self, **kwargs):
return object()

with pytest.raises(ValueError, match="not an unconsumed DLPack capsule"):
StridedMemoryView.from_dlpack(_FakeNonCapsule(), stream_ptr=-1)


class _DLPackNoMaxVersion:
"""Wraps a StridedMemoryView but rejects the ``max_version`` kwarg, forcing the
TypeError fallback in ``view_as_dlpack`` and an *unversioned* capsule import.
Expand Down
3 changes: 2 additions & 1 deletion ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ ignore = [
"UP035", # deprecated-import from typing

# security rules too strict for this project
"S101", # asserts
"S311", # allow random.* (not cryptographic)
"S404", # allow subprocess imports

Expand All @@ -81,6 +80,8 @@ known-first-party = ["cuda", "_build_shared"]
inline-quotes = "double"

[lint.per-file-ignores]
# cuda.core library code raises explicit errors; `python -O` strips asserts.
"!cuda_core/cuda/core/**" = ["S101"]
"__init__.py" = ["F401", "E402", "F403", "F405"]
"setup.py" = ["F401"]

Expand Down
Loading