-
-
Notifications
You must be signed in to change notification settings - Fork 11.1k
API: Enforce one copy for __array__
when copy=True
#26215
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 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1429,8 +1429,8 @@ _array_from_buffer_3118(PyObject *memoryview) | |
* @param writeable whether the result must be writeable. | ||
* @param context Unused parameter, must be NULL (should be removed later). | ||
* @param copy Specifies the copy behavior. | ||
* @param was_copied Set to 1 if it can be assumed that a copy was made | ||
* by implementor. | ||
* @param was_copied_by__array__ Set to 1 if it can be assumed that a copy | ||
* was made by implementor. | ||
* | ||
* @returns The array object, Py_NotImplemented if op is not array-like, | ||
* or NULL with an error set. (A new reference to Py_NotImplemented | ||
|
@@ -1439,7 +1439,7 @@ _array_from_buffer_3118(PyObject *memoryview) | |
NPY_NO_EXPORT PyObject * | ||
_array_from_array_like(PyObject *op, | ||
PyArray_Descr *requested_dtype, npy_bool writeable, PyObject *context, | ||
int copy, int *was_copied) { | ||
int copy, int *was_copied_by__array__) { | ||
PyObject* tmp; | ||
|
||
/* | ||
|
@@ -1487,7 +1487,8 @@ _array_from_array_like(PyObject *op, | |
} | ||
|
||
if (tmp == Py_NotImplemented) { | ||
tmp = PyArray_FromArrayAttr_int(op, requested_dtype, copy, was_copied); | ||
tmp = PyArray_FromArrayAttr_int( | ||
op, requested_dtype, copy, was_copied_by__array__); | ||
if (tmp == NULL) { | ||
return NULL; | ||
} | ||
|
@@ -1574,7 +1575,7 @@ PyArray_FromAny_int(PyObject *op, PyArray_Descr *in_descr, | |
|
||
// Default is copy = None | ||
int copy = -1; | ||
int was_copied = 0; | ||
int was_copied_by__array__ = 0; | ||
|
||
if (flags & NPY_ARRAY_ENSURENOCOPY) { | ||
copy = 0; | ||
|
@@ -1583,7 +1584,8 @@ PyArray_FromAny_int(PyObject *op, PyArray_Descr *in_descr, | |
} | ||
|
||
ndim = PyArray_DiscoverDTypeAndShape( | ||
op, NPY_MAXDIMS, dims, &cache, in_DType, in_descr, &dtype, copy, &was_copied); | ||
op, NPY_MAXDIMS, dims, &cache, in_DType, in_descr, &dtype, | ||
copy, &was_copied_by__array__); | ||
|
||
if (ndim < 0) { | ||
return NULL; | ||
|
@@ -1620,7 +1622,7 @@ PyArray_FromAny_int(PyObject *op, PyArray_Descr *in_descr, | |
assert(cache->converted_obj == op); | ||
arr = (PyArrayObject *)(cache->arr_or_sequence); | ||
/* we may need to cast or assert flags (e.g. copy) */ | ||
if (was_copied == 1 && flags & NPY_ARRAY_ENSURECOPY) { | ||
if (was_copied_by__array__ == 1 && flags & NPY_ARRAY_ENSURECOPY) { | ||
flags = flags & ~NPY_ARRAY_ENSURECOPY; | ||
flags = flags | NPY_ARRAY_ENSURENOCOPY; | ||
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 looks wrong, so please add a test:
should just make two copies. You probably want to make sure that users honors the 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. Ah right, a copy still needs to be made to convert to About honoring
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. No, unfortunately checking that is not really trivial (the only way to do it, is the way that the function uses later to see if it has to make a copy). 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 just realized, it is even more complicated, for subarray dtypes (maybe those should just never be passed on). But that is probably not really new, although more pronounced by nudging downstream to actually implement Maybe the solution is to just unset the dtype when a copy was made, although unfortunately that doesn't help us in the path when no copy was made and (Eplenation: if you write 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. Then let's maybe skip checking np.array(array_like, dtype=new_dtype, copy=False) errors when a np.arrray(array_like, dtype=new_dtype) get's another copy if 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.
However, maybe we should make a new issue. I am slightly worried that downstream until now always just ignored OTOH, I will also say that, I don't expect anyone to explicitly run into it, so in a sense, it is an unrelated bug fix. 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 updated flags logic here and added a test with: np.asarray(array_like, copy=True, order="F") |
||
} | ||
|
@@ -2495,14 +2497,14 @@ check_or_clear_and_warn_error_if_due_to_copy_kwarg(PyObject *kwnames) | |
* NOTE: For copy == -1 it passes `op.__array__(copy=None)`, | ||
* for copy == 0, `op.__array__(copy=False)`, and | ||
* for copy == 1, `op.__array__(copy=True). | ||
* @param was_copied Set to 1 if it can be assumed that a copy was made | ||
* by implementor. | ||
* @param was_copied_by__array__ Set to 1 if it can be assumed that a copy | ||
* was made by implementor. | ||
* @returns NotImplemented if `__array__` is not defined or a NumPy array | ||
* (or subclass). On error, return NULL. | ||
*/ | ||
NPY_NO_EXPORT PyObject * | ||
PyArray_FromArrayAttr_int( | ||
PyObject *op, PyArray_Descr *descr, int copy, int *was_copied) | ||
PyArray_FromArrayAttr_int(PyObject *op, PyArray_Descr *descr, int copy, | ||
int *was_copied_by__array__) | ||
{ | ||
PyObject *new; | ||
PyObject *array_meth; | ||
|
@@ -2589,9 +2591,10 @@ PyArray_FromArrayAttr_int( | |
Py_DECREF(new); | ||
return NULL; | ||
} | ||
if (was_copied != NULL && copy == 1 && must_copy_but_copy_kwarg_unimplemented == 0) { | ||
if (was_copied_by__array__ != NULL && copy == 1 && | ||
must_copy_but_copy_kwarg_unimplemented == 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. Could add a comment to remove the 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. Done! |
||
/* We can assume that a copy was made */ | ||
*was_copied = 1; | ||
*was_copied_by__array__ = 1; | ||
} | ||
|
||
return new; | ||
|
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.
You don't need to test this, it is OK to unset the flag if it isn't set. (to me that is clearer, but maybe it's me)
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.
Done!