Skip to content

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

Merged
ev-br merged 1 commit into
data-apis:masterfrom
antonwolfy:fix-456-dunder-dlpack-bufferror
Sep 4, 2026
Merged

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

Conversation

@antonwolfy

Copy link
Copy Markdown
Contributor

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
Contributor 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

@ev-br
ev-br merged commit 434a419 into data-apis:master Sep 4, 2026
5 checks passed
@ev-br

ev-br commented Sep 4, 2026

Copy link
Copy Markdown
Member

Let's give it a go. I'll follow up on adding an xfail in the array-api-compat CI.
Thanks @antonwolfy

@antonwolfy
antonwolfy deleted the fix-456-dunder-dlpack-bufferror branch September 6, 2026 11:24
antonwolfy added a commit to IntelPython/dpnp that referenced this pull request Sep 7, 2026
The array API conformance job skipped
`test_dlpack.py::test_dunder_dlpack` as a workaround for a bug in the
test itself (tracked in
[array-api-tests#457](data-apis/array-api-tests#457)):
the test pinned the requested `dl_device` to `kDLCPU` while drawing
`copy` from `{True, False, None}`, so on a SYCL device (reported as
`kDLOneAPI`) the `copy=False` case forced a cross-device transfer and
the spec-mandated `BufferError` was counted as a failure.

The upstream test has since been fixed to tolerate `BufferError` only
when `copy is False` and the requested device differs from the array's
own `__dlpack_device__()` — behavior dpnp already implements correctly
by delegating to dpctl. The conformance job checks out the
array-api-tests default branch without pinning a ref, so the fix is
already picked up on new runs and the skip is now dead weight.

This change removes only that entry. The remaining `tanh` special-case
skip is unrelated (an open array-api spec issue) and is left in place.
github-actions Bot added a commit to IntelPython/dpnp that referenced this pull request Sep 7, 2026
The array API conformance job skipped
`test_dlpack.py::test_dunder_dlpack` as a workaround for a bug in the
test itself (tracked in
[array-api-tests#457](data-apis/array-api-tests#457)):
the test pinned the requested `dl_device` to `kDLCPU` while drawing
`copy` from `{True, False, None}`, so on a SYCL device (reported as
`kDLOneAPI`) the `copy=False` case forced a cross-device transfer and
the spec-mandated `BufferError` was counted as a failure.

The upstream test has since been fixed to tolerate `BufferError` only
when `copy is False` and the requested device differs from the array's
own `__dlpack_device__()` — behavior dpnp already implements correctly
by delegating to dpctl. The conformance job checks out the
array-api-tests default branch without pinning a ref, so the fix is
already picked up on new runs and the skip is now dead weight.

This change removes only that entry. The remaining `tanh` special-case
skip is unrelated (an open array-api spec issue) and is left in place. 2000d87
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