Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API: Always use BufferError when dlpack export fails #22542

Merged
merged 1 commit into from Nov 7, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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