-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
MAINT/BUG: Improve error messages for dtype reassigment, fix missing DECREFs #9499
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -438,8 +438,7 @@ array_descr_set(PyArrayObject *self, PyObject *arg) | |
{ | ||
PyArray_Descr *newtype = NULL; | ||
npy_intp newdim; | ||
int i; | ||
char *msg = "new type not compatible with array."; | ||
int axis; | ||
PyObject *safe; | ||
static PyObject *checkfunc = NULL; | ||
|
||
|
@@ -461,14 +460,13 @@ array_descr_set(PyArrayObject *self, PyObject *arg) | |
if (_may_have_objects(PyArray_DESCR(self)) || _may_have_objects(newtype)) { | ||
npy_cache_import("numpy.core._internal", "_view_is_safe", &checkfunc); | ||
if (checkfunc == NULL) { | ||
return -1; | ||
goto fail; | ||
} | ||
|
||
safe = PyObject_CallFunction(checkfunc, "OO", | ||
PyArray_DESCR(self), newtype); | ||
if (safe == NULL) { | ||
Py_DECREF(newtype); | ||
return -1; | ||
goto fail; | ||
} | ||
Py_DECREF(safe); | ||
} | ||
|
@@ -492,58 +490,73 @@ array_descr_set(PyArrayObject *self, PyObject *arg) | |
} | ||
|
||
|
||
if ((newtype->elsize != PyArray_DESCR(self)->elsize) && | ||
(PyArray_NDIM(self) == 0 || | ||
!PyArray_ISONESEGMENT(self) || | ||
PyDataType_HASSUBARRAY(newtype))) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a lot of unrelated failures in here that justify their own error message. |
||
goto fail; | ||
} | ||
|
||
/* Deprecate not C contiguous and a dimension changes */ | ||
if (newtype->elsize != PyArray_DESCR(self)->elsize && | ||
!PyArray_IS_C_CONTIGUOUS(self)) { | ||
/* 11/27/2015 1.11.0 */ | ||
if (DEPRECATE("Changing the shape of non-C contiguous array by\n" | ||
"descriptor assignment is deprecated. To maintain\n" | ||
"the Fortran contiguity of a multidimensional Fortran\n" | ||
"array, use 'a.T.view(...).T' instead") < 0) { | ||
return -1; | ||
/* Changing the size of the dtype results in a shape change */ | ||
if (newtype->elsize != PyArray_DESCR(self)->elsize) { | ||
/* forbidden cases */ | ||
if (PyArray_NDIM(self) == 0) { | ||
PyErr_SetString(PyExc_ValueError, | ||
"Changing the dtype of a 0d array is only supported " | ||
"if the itemsize is unchanged"); | ||
goto fail; | ||
} | ||
} | ||
|
||
if (PyArray_IS_C_CONTIGUOUS(self)) { | ||
i = PyArray_NDIM(self) - 1; | ||
} | ||
else { | ||
i = 0; | ||
} | ||
if (newtype->elsize < PyArray_DESCR(self)->elsize) { | ||
/* | ||
* if it is compatible increase the size of the | ||
* dimension at end (or at the front for NPY_ARRAY_F_CONTIGUOUS) | ||
*/ | ||
if (PyArray_DESCR(self)->elsize % newtype->elsize != 0) { | ||
else if (PyDataType_HASSUBARRAY(newtype)) { | ||
PyErr_SetString(PyExc_ValueError, | ||
"Changing the dtype to a subarray type is only supported " | ||
"if the total itemsize is unchanged"); | ||
goto fail; | ||
} | ||
newdim = PyArray_DESCR(self)->elsize / newtype->elsize; | ||
PyArray_DIMS(self)[i] *= newdim; | ||
PyArray_STRIDES(self)[i] = newtype->elsize; | ||
} | ||
else if (newtype->elsize > PyArray_DESCR(self)->elsize) { | ||
/* | ||
* Determine if last (or first if NPY_ARRAY_F_CONTIGUOUS) dimension | ||
* is compatible | ||
*/ | ||
newdim = PyArray_DIMS(self)[i] * PyArray_DESCR(self)->elsize; | ||
if ((newdim % newtype->elsize) != 0) { | ||
|
||
/* determine which axis to resize */ | ||
if (PyArray_IS_C_CONTIGUOUS(self)) { | ||
axis = PyArray_NDIM(self) - 1; | ||
} | ||
else if (PyArray_IS_F_CONTIGUOUS(self)) { | ||
/* 2015-11-27 1.11.0, gh-6747 */ | ||
if (DEPRECATE( | ||
"Changing the shape of an F-contiguous array by " | ||
"descriptor assignment is deprecated. To maintain the " | ||
"Fortran contiguity of a multidimensional Fortran " | ||
"array, use 'a.T.view(...).T' instead") < 0) { | ||
goto fail; | ||
} | ||
axis = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This means the deprecation warning and the behavior being deprecated are now in the same place. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've never decided if we should be putting line endings in these error messages. Maybe we should have our own err calls that wrap the messages. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the line endings make things render more messily, especially since the first line gets an arbitrary prefix depending on whether its a warning or error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's why I think the error message formatter should take care of it. I don't expect anything in that direction to happen in the next decade... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO, this is not our problem, and is the concern of things higher up in the stack, starting from the python exception-printing mechanism, and extending to a line-wrapping terminal. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly ;) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I'm happy with my line-wrapping terminal dealing with it at the moment :) |
||
} | ||
else { | ||
/* Don't mention the deprecated F-contiguous support */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this comment is not particularly helpful - it assumes a future reader knows that in the past these error messages were combined. I'd suggest to remove it or make it clear that this branch is for non-contiguous arrays. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's an else clause for |
||
PyErr_SetString(PyExc_ValueError, | ||
"To change to a dtype of a different size, the array must " | ||
"be C-contiguous"); | ||
goto fail; | ||
} | ||
PyArray_DIMS(self)[i] = newdim / newtype->elsize; | ||
PyArray_STRIDES(self)[i] = newtype->elsize; | ||
|
||
if (newtype->elsize < PyArray_DESCR(self)->elsize) { | ||
/* if it is compatible, increase the size of the relevant axis */ | ||
if (PyArray_DESCR(self)->elsize % newtype->elsize != 0) { | ||
PyErr_SetString(PyExc_ValueError, | ||
"When changing to a smaller dtype, its size must be a " | ||
"divisor of the size of original dtype"); | ||
goto fail; | ||
} | ||
newdim = PyArray_DESCR(self)->elsize / newtype->elsize; | ||
PyArray_DIMS(self)[axis] *= newdim; | ||
PyArray_STRIDES(self)[axis] = newtype->elsize; | ||
} | ||
else if (newtype->elsize > PyArray_DESCR(self)->elsize) { | ||
/* if it is compatible, decrease the size of the relevant axis */ | ||
newdim = PyArray_DIMS(self)[axis] * PyArray_DESCR(self)->elsize; | ||
if ((newdim % newtype->elsize) != 0) { | ||
PyErr_SetString(PyExc_ValueError, | ||
"When changing to a larger dtype, its size must be a " | ||
"divisor of the total size in bytes of the last axis " | ||
"of the array."); | ||
goto fail; | ||
} | ||
PyArray_DIMS(self)[axis] = newdim / newtype->elsize; | ||
PyArray_STRIDES(self)[axis] = newtype->elsize; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two branches are unchanged other than in error message, but are moved within the |
||
} | ||
|
||
/* fall through -- adjust type*/ | ||
Py_DECREF(PyArray_DESCR(self)); | ||
/* Viewing as a subarray increases the number of dimensions */ | ||
if (PyDataType_HASSUBARRAY(newtype)) { | ||
/* | ||
* create new array object from data and update | ||
|
@@ -573,12 +586,12 @@ array_descr_set(PyArrayObject *self, PyObject *arg) | |
Py_DECREF(temp); | ||
} | ||
|
||
Py_DECREF(PyArray_DESCR(self)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it safe to move this? It may have already have been overwritten. In any case, it is easier to be sure if it is left where it was. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the other hand, leaving it where it was brings into question whether something was looking at a decref'd dtype. |
||
((PyArrayObject_fields *)self)->descr = newtype; | ||
PyArray_UpdateFlags(self, NPY_ARRAY_UPDATE_ALL); | ||
return 0; | ||
|
||
fail: | ||
PyErr_SetString(PyExc_ValueError, msg); | ||
Py_DECREF(newtype); | ||
return -1; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't previously decref
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch. Have to say I continue to dislike
if
-statements with side effects... (obviously outside of this PR, though)