diff options
author | Eric Wieser <wieser.eric@gmail.com> | 2018-05-18 21:34:03 -0700 |
---|---|---|
committer | Eric Wieser <wieser.eric@gmail.com> | 2018-09-23 09:25:43 -0700 |
commit | b6e03a44a219e86964a4a08f961811f7ddcca900 (patch) | |
tree | 8875eb93b4efa814104686c8291a5d52916e7c06 | |
parent | 1c0ebd808ede34d384a44ed19093afc109ba0cd8 (diff) | |
download | numpy-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.h | 32 | ||||
-rw-r--r-- | numpy/core/src/multiarray/arrayobject.c | 35 | ||||
-rw-r--r-- | numpy/core/src/multiarray/buffer.c | 3 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 12 |
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}: |