8000 ENH: Chain exceptions to give better error messages for invalid PEP3118 format strings by eric-wieser · Pull Request #11119 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH: Chain exceptions to give better error messages for invalid PEP3118 format strings #11119

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

Merged
merged 1 commit into from
Oct 3, 2018
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions numpy/core/include/numpy/npy_3kcompat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
35 changes: 2 additions & 33 deletions numpy/core/src/multiarray/arrayobject.c
10000
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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);
Expand Down Expand Up @@ -1377,7 +1346,7 @@ _failed_comparison_workaround(PyArrayObject *self, PyObject *other, int cmp_op)
/*
* Reraise the original exception, possibly chaining with a new one.
*/
PyArray_ChainExceptionsCause(exc, val, tb);
npy_PyErr_ChainExceptionsCause(exc, val, tb);
return NULL;
}

Expand Down
3 changes: 3 additions & 0 deletions numpy/core/src/multiarray/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
12 changes: 12 additions & 0 deletions numpy/core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -6662,6 +6662,18 @@ def test_error_pointer_type(self):
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}:
Expand Down
0