Skip to content

BUG: test_dunder_dlpack: accept spec-mandated BufferError for a no-copy cross-device export - #457

Open
antonwolfy wants to merge 1 commit into
data-apis:masterfrom
antonwolfy:fix-456-dunder-dlpack-bufferror
Open

BUG: test_dunder_dlpack: accept spec-mandated BufferError for a no-copy cross-device export#457
antonwolfy wants to merge 1 commit into
data-apis:masterfrom
antonwolfy:fix-456-dunder-dlpack-bufferror

Conversation

@antonwolfy

Copy link
Copy Markdown

Closes #456.

This PR proposes to tolerate BufferError only when copy is False and the requested dl_device differs from the array's own __dlpack_device__().

CPU-native backends are unaffected (cross_device is False). The copy=None/True paths must still copy and succeed, and non-BufferError exceptions still propagate.

The change is verified and passed with dpnp.

test_dunder_dlpack pins the requested dl_device to kDLCPU while drawing copy
from {True, False, None}. For a library whose array is not already on kDLCPU
(e.g. a SYCL/GPU device reported as kDLOneAPI), the copy=False case forces a
cross-device transfer. The __dlpack__ specification requires that a no-copy
cross-device transfer be refused with a BufferError, so the test was failing
on spec-compliant behavior.

Compare the requested dl_device against x.__dlpack_device__() and accept a
BufferError when copy is False and the devices differ; all other exceptions
(and the copy=None/True paths, which must copy and succeed) are still treated
as failures.

Fixes data-apis#456
@ev-br

ev-br commented Aug 20, 2026

Copy link
Copy Markdown
Member

Thanks @antonwolfy.
I tested it locally with the usual gamut of numpy, cupy, jax and torch, and it shows the following glitch for torch defaulting to CUDA as the device (in fact, I was not testing this mode previously, so thank you for pushing me towards doing the right thing) :

$ ARRAY_API_TESTS_SKIP_DTYPES=uint16,uint32,uint64 ARRAY_API_TESTS_MODULE=$VAR   pytest array_api_tests/test_dlpack.py -v --max-examples=10_000 -x
=========================================================================================== test session starts ===========================================================================================
platform linux -- Python 3.12.0, pytest-9.0.2, pluggy-1.6.0 -- /home/ev-br/.conda/envs/array-api-cupy13/bin/python3.12
cachedir: .pytest_cache
metadata: {'Python': '3.12.0', 'Platform': 'Linux-6.8.0-136-generic-x86_64-with-glibc2.39', 'Packages': {'pytest': '9.0.2', 'pluggy': '1.6.0'}, 'Plugins': {'metadata': '3.1.1', 'json-report': '1.5.0', 'hypothesis': '6.151.9'}, 'array_api_tests_module': 'array_api_tests._array_module', 'array_api_tests_version': '2026.02.26+35.ge95e8a0.dirty'}
hypothesis profile 'array-api-tests' -> max_examples=10000, deadline=timedelta(milliseconds=800)
Environment variables:
----------------------
ARRAY_API_TESTS_SKIP_DTYPES = uint16,uint32,uint64
ARRAY_API_TESTS_MODULE = exec('import torch; from array_api_compat import torch as xp; torch.set_default_device("cuda")')

Array API Tests Module: array_api_compat.torch (version unknown). API Version: 2025.12. Enabled Extensions: fft, linalg
rootdir: /home/ev-br/repos/array-api-tests
configfile: pytest.ini
plugins: metadata-3.1.1, json-report-1.5.0, hypothesis-6.151.9
collected 3 items                                                                                                                                                                                         

array_api_tests/test_dlpack.py::test_dlpack_device PASSED                                                                                                                                           [ 33%]
array_api_tests/test_dlpack.py::test_dunder_dlpack FAILED                                                                                                                                           [ 66%]

================================================================================================ FAILURES =================================================================================================
___________________________________________________________________________________________ test_dunder_dlpack ____________________________________________________________________________________________
  + Exception Group Traceback (most recent call last):
  |   File "/home/ev-br/repos/array-api-tests/array_api_tests/test_dlpack.py", line 36, in test_dunder_dlpack
  |     x=hh.arrays(dtype=hh.all_dtypes, shape=hh.shapes(min_dims=1, max_side=2)),
  |                ^^^
  |   File "/home/ev-br/.conda/envs/array-api-cupy13/lib/python3.12/site-packages/hypothesis/core.py", line 2246, in wrapped_test
  |     raise the_error_hypothesis_found
  | ExceptionGroup: Hypothesis found 2 distinct failures. (2 sub-exceptions)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/home/ev-br/repos/array-api-tests/array_api_tests/test_dlpack.py", line 60, in test_dunder_dlpack
    |     x.__dlpack__(**copy_kw, **max_version_kw, **dl_device_kw)
    |   File "/home/ev-br/.conda/envs/array-api-cupy13/lib/python3.12/site-packages/torch/_tensor.py", line 1585, in __dlpack__
    |     return handle_torch_function(Tensor.__dlpack__, (self,), *args, **kwargs)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/ev-br/.conda/envs/array-api-cupy13/lib/python3.12/site-packages/torch/overrides.py", line 1771, in handle_torch_function
    |     result = mode.__torch_function__(public_api, types, args, kwargs)
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/ev-br/.conda/envs/array-api-cupy13/lib/python3.12/site-packages/torch/utils/_device.py", line 122, in __torch_function__
    |     return func(*args, **kwargs)
    |            ^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/ev-br/.conda/envs/array-api-cupy13/lib/python3.12/site-packages/torch/_tensor.py", line 1667, in __dlpack__
    |     return _C._to_dlpack_versioned(self, dl_device=dl_device, copy=copy)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | ValueError: cannot move (i.e. copy=False) tensor from cuda:0 to cpu without copying.
    | 
    | ========== FAILING CODE SNIPPET:
    | tensor([], device='cuda:0', dtype=torch.bool).__dlpack__() with copy_kw = {'copy': False}, max_version_kw = {'max_version': (1, 0)} and dl_device_kw = {'dl_device': (1, 0)}
    | ====================
    | 
    | Falsifying example: test_dunder_dlpack(
    |     x=tensor([], device='cuda:0', dtype=torch.bool),  # or any other generated value
    |     copy_kw={'copy': False},
    |     max_version_kw={'max_version': (1, 0)},  # or any other generated value
    |     dl_device_kw={'dl_device': (1, 0)},
    |     data=data(...),
    | )
    +---------------- 2 ----------------
    | Traceback (most recent call last):
    |   File "/home/ev-br/repos/array-api-tests/array_api_tests/test_dlpack.py", line 60, in test_dunder_dlpack
    |     x.__dlpack__(**copy_kw, **max_version_kw, **dl_device_kw)
    |   File "/home/ev-br/.conda/envs/array-api-cupy13/lib/python3.12/site-packages/torch/_tensor.py", line 1585, in __dlpack__
    |     return handle_torch_function(Tensor.__dlpack__, (self,), *args, **kwargs)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/ev-br/.conda/envs/array-api-cupy13/lib/python3.12/site-packages/torch/overrides.py", line 1771, in handle_torch_function
    |     result = mode.__torch_function__(public_api, types, args, kwargs)
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/ev-br/.conda/envs/array-api-cupy13/lib/python3.12/site-packages/torch/utils/_device.py", line 122, in __torch_function__
    |     return func(*args, **kwargs)
    |            ^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/ev-br/.conda/envs/array-api-cupy13/lib/python3.12/site-packages/torch/_tensor.py", line 1665, in __dlpack__
    |     return _C._to_dlpack(self, dl_device=dl_device, copy=copy)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | ValueError: cannot move (i.e. copy=False) tensor from cuda:0 to cpu without copying.
    | 
    | ========== FAILING CODE SNIPPET:
    | tensor([], device='cuda:0', dtype=torch.bool).__dlpack__() with copy_kw = {'copy': False}, max_version_kw = {} and dl_device_kw = {'dl_device': (1, 0)}
    | ====================
    | 
    | Falsifying example: test_dunder_dlpack(
    |     x=tensor([], device='cuda:0', dtype=torch.bool),  # or any other generated value
    |     copy_kw={'copy': False},
    |     max_version_kw={},  # or any other generated value
    |     dl_device_kw={'dl_device': (1, 0)},
    |     data=data(...),
    | )
    +------------------------------------

From the test output, the failure is

In [1]: import torch

In [2]: x = torch.as_tensor([], device='cuda:0', dtype=torch.bool)

In [3]: x.__dlpack__(copy=False, dl_device=(1,0))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[3], line 1
----> 1 x.__dlpack__(copy=False, dl_device=(1,0))

File ~/.conda/envs/array-api-cupy13/lib/python3.12/site-packages/torch/_tensor.py:1665, in Tensor.__dlpack__(self, stream, max_version, dl_device, copy)
   1661     return xla_dlpack.to_dlpack(self)
   1663 if max_version is None or max_version[0] < 1:
   1664     # Fallback to the old, unversioned variant.
-> 1665     return _C._to_dlpack(self, dl_device=dl_device, copy=copy)
   1667 return _C._to_dlpack_versioned(self, dl_device=dl_device, copy=copy)

ValueError: cannot move (i.e. copy=False) tensor from cuda:0 to cpu without copying.

To make sure I understand it right. So what it says is that torch is not compliant: it emits a ValueError instead of the spec-mandated BufferError?

@antonwolfy

Copy link
Copy Markdown
Author

Yes, per my understanding. Every spec version mandates BufferError for a copy=False cross-device export:

If False, the function must never copy, and raise a BufferError in case a copy is deemed necessary (e.g. if a cross-device data movement is requested, and it is not possible without a copy).

@ev-br

ev-br commented Aug 20, 2026

Copy link
Copy Markdown
Member

Thanks for confirming. I opened pytorch/pytorch#194267

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test_dunder_dlpack hard-pins dl_device to kDLCPU, so it fails for any non-CPU-native library

2 participants