summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Wieser <wieser.eric@gmail.com>2018-05-18 21:34:03 -0700
committerEric Wieser <wieser.eric@gmail.com>2018-09-23 09:25:43 -0700
commitb6e03a44a219e86964a4a08f961811f7ddcca900 (patch)
tree8875eb93b4efa814104686c8291a5d52916e7c06
parent1c0ebd808ede34d384a44ed19093afc109ba0cd8 (diff)
downloadnumpy-b6e03a44a219e86964a4a08f961811f7ddcca900.tar.gz
ENH: Chain exceptions to give better error messages for invalid PEP3118 format strings
Sample console output: ``` In [1]: from ctypes import c_wchar In [2]: x = c_wchar * 3 In [3]: np.array(x()) --------------------------------------------------------------------------- ValueError Traceback (most recent call last) ~\Repos\numeric-python\numpy\build\testenv\Lib\site-packages\numpy\core\_internal.py in _dtype_from_pep3118(spec) 486 --> 487 dtype, align = __dtype_from_pep3118(stream, is_subdtype=False) 488 return dtype ~\Repos\numeric-python\numpy\build\testenv\Lib\site-packages\numpy\core\_internal.py in __dtype_from_pep3118(stream, is_subdtype) 558 else: --> 559 raise ValueError("Unknown PEP 3118 data type specifier %r" % stream.s) 560 ValueError: Unknown PEP 3118 data type specifier 'u' The above exception was the direct cause of the following exception: ValueError Traceback (most recent call last) <ipython-input-3-f8a17df3aa24> in <module>() ----> 1 np.array(x()) ValueError: '<u' is not a valid PEP 3118 buffer format string ```
-rw-r--r--numpy/core/include/numpy/npy_3kcompat.h32
-rw-r--r--numpy/core/src/multiarray/arrayobject.c35
-rw-r--r--numpy/core/src/multiarray/buffer.c3
-rw-r--r--numpy/core/tests/test_multiarray.py12
4 files changed, 49 insertions, 33 deletions
diff --git a/numpy/core/include/numpy/npy_3kcompat.h b/numpy/core/include/numpy/npy_3kcompat.h
index 2d0ccd3b9..808518266 100644
--- a/numpy/core/include/numpy/npy_3kcompat.h
+++ b/numpy/core/include/numpy/npy_3kcompat.h
@@ -378,6 +378,38 @@ npy_PyFile_CloseFile(PyObject *file)
return 0;
}
+
+/* This is a copy of _PyErr_ChainExceptions, with:
+ * - a minimal implementation for python 2
+ * - __cause__ used instead of __context__
+ */
+static NPY_INLINE void
+npy_PyErr_ChainExceptionsCause(PyObject *exc, PyObject *val, PyObject *tb)
+{
+ if (exc == NULL)
+ return;
+
+ if (PyErr_Occurred()) {
+ /* only py3 supports this anyway */
+ #ifdef NPY_PY3K
+ PyObject *exc2, *val2, *tb2;
+ PyErr_Fetch(&exc2, &val2, &tb2);
+ PyErr_NormalizeException(&exc, &val, &tb);
+ if (tb != NULL) {
+ PyException_SetTraceback(val, tb);
+ Py_DECREF(tb);
+ }
+ Py_DECREF(exc);
+ PyErr_NormalizeException(&exc2, &val2, &tb2);
+ PyException_SetCause(val2, val);
+ PyErr_Restore(exc2, val2, tb2);
+ #endif
+ }
+ else {
+ PyErr_Restore(exc, val, tb);
+ }
+}
+
/*
* PyObject_Cmp
*/
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c
index 368f5ded7..341682588 100644
--- a/numpy/core/src/multiarray/arrayobject.c
+++ b/numpy/core/src/multiarray/arrayobject.c
@@ -1218,37 +1218,6 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op)
}
}
-/* This is a copy of _PyErr_ChainExceptions, with:
- * - a minimal implementation for python 2
- * - __cause__ used instead of __context__
- */
-NPY_NO_EXPORT void
-PyArray_ChainExceptionsCause(PyObject *exc, PyObject *val, PyObject *tb)
-{
- if (exc == NULL)
- return;
-
- if (PyErr_Occurred()) {
- /* only py3 supports this anyway */
- #ifdef NPY_PY3K
- PyObject *exc2, *val2, *tb2;
- PyErr_Fetch(&exc2, &val2, &tb2);
- PyErr_NormalizeException(&exc, &val, &tb);
- if (tb != NULL) {
- PyException_SetTraceback(val, tb);
- Py_DECREF(tb);
- }
- Py_DECREF(exc);
- PyErr_NormalizeException(&exc2, &val2, &tb2);
- PyException_SetCause(val2, val);
- PyErr_Restore(exc2, val2, tb2);
- #endif
- }
- else {
- PyErr_Restore(exc, val, tb);
- }
-}
-
/*
* Silence the current error and emit a deprecation warning instead.
*
@@ -1260,7 +1229,7 @@ DEPRECATE_silence_error(const char *msg) {
PyObject *exc, *val, *tb;
PyErr_Fetch(&exc, &val, &tb);
if (DEPRECATE(msg) < 0) {
- PyArray_ChainExceptionsCause(exc, val, tb);
+ npy_PyErr_ChainExceptionsCause(exc, val, tb);
return -1;
}
Py_XDECREF(exc);
@@ -1377,7 +1346,7 @@ fail:
/*
* Reraise the original exception, possibly chaining with a new one.
*/
- PyArray_ChainExceptionsCause(exc, val, tb);
+ npy_PyErr_ChainExceptionsCause(exc, val, tb);
return NULL;
}
diff --git a/numpy/core/src/multiarray/buffer.c b/numpy/core/src/multiarray/buffer.c
index c8e3da8bc..51115ab53 100644
--- a/numpy/core/src/multiarray/buffer.c
+++ b/numpy/core/src/multiarray/buffer.c
@@ -989,8 +989,11 @@ _descriptor_from_pep3118_format(char *s)
Py_DECREF(str);
Py_DECREF(_numpy_internal);
if (descr == NULL) {
+ PyObject *exc, *val, *tb;
+ PyErr_Fetch(&exc, &val, &tb);
PyErr_Format(PyExc_ValueError,
"'%s' is not a valid PEP 3118 buffer format string", buf);
+ npy_PyErr_ChainExceptionsCause(exc, val, tb);
free(buf);
return NULL;
}
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 277f64a5d..98482bb16 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -6662,6 +6662,18 @@ class TestNewBufferProtocol(object):
ValueError, "format string",
np.array, m)
+ def test_error_message(self):
+ # wchar has no corresponding numpy type - if this changes in future, we
+ # need a better way to construct an invalid memoryview format.
+ t = ctypes.c_wchar * 4
+ with assert_raises(ValueError) as cm:
+ np.array(t())
+
+ exc = cm.exception
+ if sys.version_info.major > 2:
+ with assert_raises_regex(ValueError, "Unknown .* specifier 'u'"):
+ raise exc.__cause__
+
def test_ctypes_integer_via_memoryview(self):
# gh-11150, due to bpo-10746
for c_integer in {ctypes.c_int, ctypes.c_long, ctypes.c_longlong}: