Skip to content

Commit

Permalink
API: Always use BufferError when dlpack export fails
Browse files Browse the repository at this point in the history
See also data-apis/array-api#498.

I think we should just change this.  It is a niche feature and just
an error type change.

Closes gh-20742
  • Loading branch information
seberg committed Nov 7, 2022
1 parent fcafb65 commit 26342d7
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 19 deletions.
7 changes: 7 additions & 0 deletions doc/release/upcoming_changes/22542.compatibility.rst
@@ -0,0 +1,7 @@
DLPack export raises ``BufferError``
------------------------------------
When an array buffer cannot be exported via DLPack a
``BufferError`` is now always raised where previously
``TypeError`` or ``RuntimeError`` was raised.
This allows falling back to the buffer protocol or
``__array_interface__`` when DLPack was tried first.
31 changes: 17 additions & 14 deletions numpy/core/src/multiarray/dlpack.c
Expand Up @@ -133,14 +133,15 @@ array_dlpack(PyArrayObject *self,
}

if (stream != Py_None) {
PyErr_SetString(PyExc_RuntimeError, "NumPy only supports "
"stream=None.");
PyErr_SetString(PyExc_RuntimeError,
"NumPy only supports stream=None.");
return NULL;
}

if ( !(PyArray_FLAGS(self) & NPY_ARRAY_WRITEABLE)) {
PyErr_SetString(PyExc_TypeError, "NumPy currently only supports "
"dlpack for writeable arrays");
PyErr_SetString(PyExc_BufferError,
"Cannot export readonly array since signalling readonly "
"is unsupported by DLPack.");
return NULL;
}

Expand All @@ -152,7 +153,7 @@ array_dlpack(PyArrayObject *self,
if (!PyArray_IS_C_CONTIGUOUS(self) && PyArray_SIZE(self) != 1) {
for (int i = 0; i < ndim; ++i) {
if (shape[i] != 1 && strides[i] % itemsize != 0) {
PyErr_SetString(PyExc_RuntimeError,
PyErr_SetString(PyExc_BufferError,
"DLPack only supports strides which are a multiple of "
"itemsize.");
return NULL;
Expand All @@ -164,8 +165,8 @@ array_dlpack(PyArrayObject *self,
PyArray_Descr *dtype = PyArray_DESCR(self);

if (PyDataType_ISBYTESWAPPED(dtype)) {
PyErr_SetString(PyExc_TypeError, "DLPack only supports native "
"byte swapping.");
PyErr_SetString(PyExc_BufferError,
"DLPack only supports native byte order.");
return NULL;
}

Expand All @@ -182,8 +183,9 @@ array_dlpack(PyArrayObject *self,
// We can't be sure that the dtype is
// IEEE or padded.
if (itemsize > 8) {
PyErr_SetString(PyExc_TypeError, "DLPack only supports IEEE "
"floating point types without padding.");
PyErr_SetString(PyExc_BufferError,
"DLPack only supports IEEE floating point types "
"without padding (longdouble typically is not IEEE).");
return NULL;
}
managed_dtype.code = kDLFloat;
Expand All @@ -192,16 +194,17 @@ array_dlpack(PyArrayObject *self,
// We can't be sure that the dtype is
// IEEE or padded.
if (itemsize > 16) {
PyErr_SetString(PyExc_TypeError, "DLPack only supports IEEE "
"complex point types without padding.");
PyErr_SetString(PyExc_BufferError,
"DLPack only supports IEEE floating point types "
"without padding (longdouble typically is not IEEE).");
return NULL;
}
managed_dtype.code = kDLComplex;
}
else {
PyErr_SetString(PyExc_TypeError,
"DLPack only supports signed/unsigned integers, float "
"and complex dtypes.");
PyErr_SetString(PyExc_BufferError,
"DLPack only supports signed/unsigned integers, float "
"and complex dtypes.");
return NULL;
}

Expand Down
10 changes: 5 additions & 5 deletions numpy/core/tests/test_dlpack.py
Expand Up @@ -26,7 +26,7 @@ def test_strides_not_multiple_of_itemsize(self):
y = np.zeros((5,), dtype=dt)
z = y['int']

with pytest.raises(RuntimeError):
with pytest.raises(BufferError):
np.from_dlpack(z)

@pytest.mark.skipif(IS_PYPY, reason="PyPy can't get refcounts.")
Expand All @@ -53,14 +53,14 @@ def test_dtype_passthrough(self, dtype):
def test_invalid_dtype(self):
x = np.asarray(np.datetime64('2021-05-27'))

with pytest.raises(TypeError):
with pytest.raises(BufferError):
np.from_dlpack(x)

def test_invalid_byte_swapping(self):
dt = np.dtype('=i8').newbyteorder()
x = np.arange(5, dtype=dt)

with pytest.raises(TypeError):
with pytest.raises(BufferError):
np.from_dlpack(x)

def test_non_contiguous(self):
Expand Down Expand Up @@ -100,15 +100,15 @@ def dlpack_deleter_exception(self):
x = np.arange(5)
_ = x.__dlpack__()
raise RuntimeError

def test_dlpack_destructor_exception(self):
with pytest.raises(RuntimeError):
self.dlpack_deleter_exception()

def test_readonly(self):
x = np.arange(5)
x.flags.writeable = False
with pytest.raises(TypeError):
with pytest.raises(BufferError):
x.__dlpack__()

def test_ndim0(self):
Expand Down

0 comments on commit 26342d7

Please sign in to comment.