Skip to content

Commit c72fa87

Browse files
committed
Coerce stream_ptr to int in view_as_dlpack
1 parent 9d903cd commit c72fa87

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

‎cuda_core/cuda/core/_memoryview.pyx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,10 @@ cdef StridedMemoryView view_as_dlpack(obj, stream_ptr, view=None):
10341034
cdef int dldevice, device_id
10351035
cdef bint is_device_accessible, is_readonly = False
10361036
is_device_accessible = False
1037+
# Callers may pass an int-convertible stream wrapper (e.g. numba-cuda's
1038+
# CUstream). Normalize it so the -1 and truthiness checks below see an int.
1039+
if stream_ptr is not None:
1040+
stream_ptr = int(stream_ptr)
10371041
dldevice, device_id = obj.__dlpack_device__()
10381042
if dldevice == _kDLCPU:
10391043
assert device_id == 0

‎cuda_core/tests/test_utils_dlpack.py‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,66 @@ def test_from_dlpack_cpu_stream_none_ambiguous():
135135
StridedMemoryView.from_dlpack(src, stream_ptr=None)
136136

137137

138+
class _StreamHandleLike:
139+
"""Stand-in for a third-party stream wrapper such as numba-cuda's ``CUstream``.
140+
141+
It is int-convertible but has no ``__bool__``/``__len__``, so it is always
142+
truthy even when it wraps 0.
143+
"""
144+
145+
def __init__(self, value):
146+
self._value = value
147+
148+
def __int__(self):
149+
return self._value
150+
151+
152+
class _RecordingDLPackSource:
153+
"""CPU DLPack exporter that records the ``stream`` it was asked for."""
154+
155+
def __init__(self, array):
156+
self._array = array
157+
self.streams = []
158+
159+
def __dlpack_device__(self):
160+
return self._array.__dlpack_device__()
161+
162+
def __dlpack__(self, *, stream=None, **kwargs):
163+
self.streams.append(stream)
164+
return self._array.__dlpack__(stream=stream, **kwargs)
165+
166+
167+
def _view_via_constructor(src, stream_ptr):
168+
with pytest.warns(DeprecationWarning, match="Constructing a StridedMemoryView directly"):
169+
return StridedMemoryView(src, stream_ptr)
170+
171+
172+
@pytest.mark.agent_authored(model="claude-sonnet-5-5")
173+
@pytest.mark.parametrize("handle_value", [0, -1], ids=["zero", "opt-out"])
174+
@pytest.mark.parametrize(
175+
"make_view",
176+
[
177+
StridedMemoryView.from_dlpack,
178+
StridedMemoryView.from_any_interface,
179+
_view_via_constructor,
180+
],
181+
ids=["from_dlpack", "from_any_interface", "constructor"],
182+
)
183+
def test_cpu_dlpack_accepts_int_convertible_stream_ptr(make_view, handle_value):
184+
"""A truthy int-convertible stream wrapper is coerced before ``__dlpack__``.
185+
186+
CPU exporters such as NumPy only accept ``stream=None``. A wrapper around
187+
0 (or the -1 opt-out) must be treated as the integer it wraps, not as an
188+
opaque truthy object forwarded to ``__dlpack__`` as a stream.
189+
"""
190+
src = _RecordingDLPackSource(np.arange(4, dtype=np.float32))
191+
192+
view = make_view(src, _StreamHandleLike(handle_value))
193+
194+
assert src.streams == [None]
195+
assert view.shape == (4,)
196+
197+
138198
def test_from_dlpack_unsupported_device_type():
139199
"""``view_as_dlpack`` rejects a DLPack device that is neither CPU, CUDA,
140200
CUDA-pinned, nor CUDA-managed before ever calling ``__dlpack__``."""

0 commit comments

Comments
 (0)