summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h2
-rw-r--r--numpy/core/overrides.py6
-rw-r--r--numpy/core/src/multiarray/_multiarray_tests.c.src15
-rw-r--r--numpy/core/src/multiarray/array_method.c65
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c207
-rw-r--r--numpy/core/src/multiarray/datetime.c3
-rw-r--r--numpy/core/tests/test_api.py13
-rw-r--r--numpy/core/tests/test_multiarray.py7
-rw-r--r--numpy/lib/index_tricks.py6
9 files changed, 215 insertions, 109 deletions
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index 58a5aba47..6626da1e9 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -1244,6 +1244,8 @@ struct PyArrayIterObject_tag {
_PyAIT(it)->dataptr = PyArray_BYTES(_PyAIT(it)->ao); \
for (__npy_i = 0; __npy_i<=_PyAIT(it)->nd_m1; \
__npy_i++) { \
+ _PyAIT(it)->coordinates[__npy_i] = \
+ (__npy_ind / _PyAIT(it)->factors[__npy_i]); \
_PyAIT(it)->dataptr += \
(__npy_ind / _PyAIT(it)->factors[__npy_i]) \
* _PyAIT(it)->strides[__npy_i]; \
diff --git a/numpy/core/overrides.py b/numpy/core/overrides.py
index c2b5fb7fa..70085d896 100644
--- a/numpy/core/overrides.py
+++ b/numpy/core/overrides.py
@@ -18,11 +18,7 @@ array_function_like_doc = (
NumPy arrays. If an array-like passed in as ``like`` supports
the ``__array_function__`` protocol, the result will be defined
by it. In this case, it ensures the creation of an array object
- compatible with that passed in via this argument.
-
- .. note::
- The ``like`` keyword is an experimental feature pending on
- acceptance of :ref:`NEP 35 <NEP35>`."""
+ compatible with that passed in via this argument."""
)
def set_array_function_like_doc(public_api):
diff --git a/numpy/core/src/multiarray/_multiarray_tests.c.src b/numpy/core/src/multiarray/_multiarray_tests.c.src
index bfdeae079..79140bdb7 100644
--- a/numpy/core/src/multiarray/_multiarray_tests.c.src
+++ b/numpy/core/src/multiarray/_multiarray_tests.c.src
@@ -87,7 +87,7 @@ static int copy_@name@(PyArrayIterObject *itx, PyArrayNeighborhoodIterObject *ni
* For each point in itx, copy the current neighborhood into an array which
* is appended at the output list
*/
- for (i = 0; i < itx->size; ++i) {
+ for (i = itx->index; i < itx->size; ++i) {
PyArrayNeighborhoodIter_Reset(niterx);
for (j = 0; j < PyArray_NDIM(itx->ao); ++j) {
@@ -130,7 +130,7 @@ static int copy_object(PyArrayIterObject *itx, PyArrayNeighborhoodIterObject *ni
* For each point in itx, copy the current neighborhood into an array which
* is appended at the output list
*/
- for (i = 0; i < itx->size; ++i) {
+ for (i = itx->index; i < itx->size; ++i) {
PyArrayNeighborhoodIter_Reset(niterx);
for (j = 0; j < PyArray_NDIM(itx->ao); ++j) {
@@ -161,10 +161,11 @@ test_neighborhood_iterator(PyObject* NPY_UNUSED(self), PyObject* args)
PyArrayObject *ax, *afill;
PyArrayIterObject *itx;
int i, typenum, mode, st;
+ Py_ssize_t idxstart = 0;
npy_intp bounds[NPY_MAXDIMS*2];
PyArrayNeighborhoodIterObject *niterx;
- if (!PyArg_ParseTuple(args, "OOOi", &x, &b, &fill, &mode)) {
+ if (!PyArg_ParseTuple(args, "OOOi|n", &x, &b, &fill, &mode, &idxstart)) {
return NULL;
}
@@ -224,12 +225,20 @@ test_neighborhood_iterator(PyObject* NPY_UNUSED(self), PyObject* args)
}
}
+ if (idxstart >= itx->size) {
+ PyErr_SetString(PyExc_ValueError,
+ "start index not compatible with x input");
+ goto clean_itx;
+ }
+
niterx = (PyArrayNeighborhoodIterObject*)PyArray_NeighborhoodIterNew(
(PyArrayIterObject*)itx, bounds, mode, afill);
if (niterx == NULL) {
goto clean_afill;
}
+ PyArray_ITER_GOTO1D((PyArrayIterObject*)itx, idxstart);
+
switch (typenum) {
case NPY_OBJECT:
st = copy_object(itx, niterx, bounds, &out);
diff --git a/numpy/core/src/multiarray/array_method.c b/numpy/core/src/multiarray/array_method.c
index e13da12de..3ecc20d1d 100644
--- a/numpy/core/src/multiarray/array_method.c
+++ b/numpy/core/src/multiarray/array_method.c
@@ -210,10 +210,12 @@ validate_spec(PyArrayMethod_Spec *spec)
case NPY_UNSAFE_CASTING:
break;
default:
- PyErr_Format(PyExc_TypeError,
- "ArrayMethod has invalid casting `%d`. (method: %s)",
- spec->casting, spec->name);
- return -1;
+ if (spec->casting != -1) {
+ PyErr_Format(PyExc_TypeError,
+ "ArrayMethod has invalid casting `%d`. (method: %s)",
+ spec->casting, spec->name);
+ return -1;
+ }
}
for (int i = 0; i < nargs; i++) {
@@ -301,6 +303,13 @@ fill_arraymethod_from_slots(
/* Check whether the slots are valid: */
if (meth->resolve_descriptors == &default_resolve_descriptors) {
+ if (spec->casting == -1) {
+ PyErr_Format(PyExc_TypeError,
+ "Cannot set casting to -1 (invalid) when not providing "
+ "the default `resolve_descriptors` function. "
+ "(method: %s)", spec->name);
+ return -1;
+ }
for (int i = 0; i < meth->nin + meth->nout; i++) {
if (res->dtypes[i] == NULL) {
if (i < meth->nin) {
@@ -573,6 +582,8 @@ boundarraymethod__resolve_descripors(
/*
* The casting flags should be the most generic casting level (except the
* cast-is-view flag. If no input is parametric, it must match exactly.
+ *
+ * (Note that these checks are only debugging checks.)
*/
int parametric = 0;
for (int i = 0; i < nin + nout; i++) {
@@ -581,34 +592,34 @@ boundarraymethod__resolve_descripors(
break;
}
}
- if (!parametric) {
- /*
- * Non-parametric can only mismatch if it switches from no to equiv
- * (e.g. due to byteorder changes).
- */
- if (self->method->casting != (casting & ~_NPY_CAST_IS_VIEW) &&
- !(self->method->casting == NPY_NO_CASTING &&
- casting == NPY_EQUIV_CASTING)) {
- PyErr_Format(PyExc_RuntimeError,
- "resolve_descriptors cast level did not match stored one "
- "(expected %d, got %d) for method %s",
- self->method->casting, (casting & ~_NPY_CAST_IS_VIEW),
- self->method->name);
- Py_DECREF(result_tuple);
- return NULL;
- }
- }
- else {
+ if (self->method->casting != -1) {
NPY_CASTING cast = casting & ~_NPY_CAST_IS_VIEW;
- if (cast != PyArray_MinCastSafety(cast, self->method->casting)) {
+ if (self->method->casting !=
+ PyArray_MinCastSafety(cast, self->method->casting)) {
PyErr_Format(PyExc_RuntimeError,
- "resolve_descriptors cast level did not match stored one "
- "(expected %d, got %d) for method %s",
- self->method->casting, (casting & ~_NPY_CAST_IS_VIEW),
- self->method->name);
+ "resolve_descriptors cast level did not match stored one. "
+ "(set level is %d, got %d for method %s)",
+ self->method->casting, cast, self->method->name);
Py_DECREF(result_tuple);
return NULL;
}
+ if (!parametric) {
+ /*
+ * Non-parametric can only mismatch if it switches from equiv to no
+ * (e.g. due to byteorder changes).
+ */
+ if (cast != self->method->casting &&
+ self->method->casting != NPY_EQUIV_CASTING) {
+ PyErr_Format(PyExc_RuntimeError,
+ "resolve_descriptors cast level changed even though "
+ "the cast is non-parametric where the only possible "
+ "change should be from equivalent to no casting. "
+ "(set level is %d, got %d for method %s)",
+ self->method->casting, cast, self->method->name);
+ Py_DECREF(result_tuple);
+ return NULL;
+ }
+ }
}
return Py_BuildValue("iN", casting, result_tuple);
diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c
index 01ee56d16..1a962ef78 100644
--- a/numpy/core/src/multiarray/convert_datatype.c
+++ b/numpy/core/src/multiarray/convert_datatype.c
@@ -358,6 +358,45 @@ PyArray_CastAnyTo(PyArrayObject *out, PyArrayObject *mp)
}
+static NPY_CASTING
+_get_cast_safety_from_castingimpl(PyArrayMethodObject *castingimpl,
+ PyArray_DTypeMeta *dtypes[2], PyArray_Descr *from, PyArray_Descr *to)
+{
+ PyArray_Descr *descrs[2] = {from, to};
+ PyArray_Descr *out_descrs[2];
+
+ NPY_CASTING casting = castingimpl->resolve_descriptors(
+ castingimpl, dtypes, descrs, out_descrs);
+ if (casting < 0) {
+ return -1;
+ }
+ /* The returned descriptors may not match, requiring a second check */
+ if (out_descrs[0] != descrs[0]) {
+ NPY_CASTING from_casting = PyArray_GetCastSafety(
+ descrs[0], out_descrs[0], NULL);
+ casting = PyArray_MinCastSafety(casting, from_casting);
+ if (casting < 0) {
+ goto finish;
+ }
+ }
+ if (descrs[1] != NULL && out_descrs[1] != descrs[1]) {
+ NPY_CASTING from_casting = PyArray_GetCastSafety(
+ descrs[1], out_descrs[1], NULL);
+ casting = PyArray_MinCastSafety(casting, from_casting);
+ if (casting < 0) {
+ goto finish;
+ }
+ }
+
+ finish:
+ Py_DECREF(out_descrs[0]);
+ Py_DECREF(out_descrs[1]);
+ /* NPY_NO_CASTING has to be used for (NPY_EQUIV_CASTING|_NPY_CAST_IS_VIEW) */
+ assert(casting != (NPY_EQUIV_CASTING|_NPY_CAST_IS_VIEW));
+ return casting;
+}
+
+
/**
* Given two dtype instances, find the correct casting safety.
*
@@ -375,7 +414,6 @@ NPY_NO_EXPORT NPY_CASTING
PyArray_GetCastSafety(
PyArray_Descr *from, PyArray_Descr *to, PyArray_DTypeMeta *to_dtype)
{
- NPY_CASTING casting;
if (to != NULL) {
to_dtype = NPY_DTYPE(to);
}
@@ -389,41 +427,67 @@ PyArray_GetCastSafety(
}
PyArrayMethodObject *castingimpl = (PyArrayMethodObject *)meth;
-
PyArray_DTypeMeta *dtypes[2] = {NPY_DTYPE(from), to_dtype};
- PyArray_Descr *descrs[2] = {from, to};
- PyArray_Descr *out_descrs[2];
-
- casting = castingimpl->resolve_descriptors(
- castingimpl, dtypes, descrs, out_descrs);
+ NPY_CASTING casting = _get_cast_safety_from_castingimpl(castingimpl,
+ dtypes, from, to);
Py_DECREF(meth);
- if (casting < 0) {
+
+ return casting;
+}
+
+
+/**
+ * Check whether a cast is safe, see also `PyArray_GetCastSafety` for
+ * a similiar function. Unlike GetCastSafety, this function checks the
+ * `castingimpl->casting` when available. This allows for two things:
+ *
+ * 1. It avoids calling `resolve_descriptors` in some cases.
+ * 2. Strings need to discover the length, but in some cases we know that the
+ * cast is valid (assuming the string length is discovered first).
+ *
+ * The latter means that a `can_cast` could return True, but the cast fail
+ * because the parametric type cannot guess the correct output descriptor.
+ * (I.e. if `object_arr.astype("S")` did _not_ inspect the objects, and the
+ * user would have to guess the string length.)
+ *
+ * @param casting the requested casting safety.
+ * @param from
+ * @param to The descriptor to cast to (may be NULL)
+ * @param to_dtype If `to` is NULL, must pass the to_dtype (otherwise this
+ * is ignored).
+ * @return 0 for an invalid cast, 1 for a valid and -1 for an error.
+ */
+static int
+PyArray_CheckCastSafety(NPY_CASTING casting,
+ PyArray_Descr *from, PyArray_Descr *to, PyArray_DTypeMeta *to_dtype)
+{
+ if (to != NULL) {
+ to_dtype = NPY_DTYPE(to);
+ }
+ PyObject *meth = PyArray_GetCastingImpl(NPY_DTYPE(from), to_dtype);
+ if (meth == NULL) {
return -1;
}
- /* The returned descriptors may not match, requiring a second check */
- if (out_descrs[0] != descrs[0]) {
- NPY_CASTING from_casting = PyArray_GetCastSafety(
- descrs[0], out_descrs[0], NULL);
- casting = PyArray_MinCastSafety(casting, from_casting);
- if (casting < 0) {
- goto finish;
- }
+ if (meth == Py_None) {
+ Py_DECREF(Py_None);
+ return -1;
}
- if (descrs[1] != NULL && out_descrs[1] != descrs[1]) {
- NPY_CASTING from_casting = PyArray_GetCastSafety(
- descrs[1], out_descrs[1], NULL);
- casting = PyArray_MinCastSafety(casting, from_casting);
- if (casting < 0) {
- goto finish;
- }
+ PyArrayMethodObject *castingimpl = (PyArrayMethodObject *)meth;
+
+ if (PyArray_MinCastSafety(castingimpl->casting, casting) == casting) {
+ /* No need to check using `castingimpl.resolve_descriptors()` */
+ return 1;
}
- finish:
- Py_DECREF(out_descrs[0]);
- Py_DECREF(out_descrs[1]);
- /* NPY_NO_CASTING has to be used for (NPY_EQUIV_CASTING|_NPY_CAST_IS_VIEW) */
- assert(casting != (NPY_EQUIV_CASTING|_NPY_CAST_IS_VIEW));
- return casting;
+ PyArray_DTypeMeta *dtypes[2] = {NPY_DTYPE(from), to_dtype};
+ NPY_CASTING safety = _get_cast_safety_from_castingimpl(castingimpl,
+ dtypes, from, to);
+ Py_DECREF(meth);
+ /* If casting is the smaller (or equal) safety we match */
+ if (safety < 0) {
+ return -1;
+ }
+ return PyArray_MinCastSafety(safety, casting) == casting;
}
@@ -565,6 +629,8 @@ NPY_NO_EXPORT npy_bool
PyArray_CanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to,
NPY_CASTING casting)
{
+ PyArray_DTypeMeta *to_dtype = NPY_DTYPE(to);
+
/*
* NOTE: This code supports U and S, this is identical to the code
* in `ctors.c` which does not allow these dtypes to be attached
@@ -576,21 +642,21 @@ PyArray_CanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to,
* TODO: We should grow support for `np.can_cast("d", "S")` being
* different from `np.can_cast("d", "S0")` here, at least for
* the python side API.
+ * The `to = NULL` branch, which considers "S0" to be "flexible"
+ * should probably be deprecated.
+ * (This logic is duplicated in `PyArray_CanCastArrayTo`)
*/
- NPY_CASTING safety;
if (PyDataType_ISUNSIZED(to) && to->subarray == NULL) {
- safety = PyArray_GetCastSafety(from, NULL, NPY_DTYPE(to));
- }
- else {
- safety = PyArray_GetCastSafety(from, to, NPY_DTYPE(to));
+ to = NULL; /* consider mainly S0 and U0 as S and U */
}
- if (safety < 0) {
+ int is_valid = PyArray_CheckCastSafety(casting, from, to, to_dtype);
+ /* Clear any errors and consider this unsafe (should likely be changed) */
+ if (is_valid < 0) {
PyErr_Clear();
return 0;
}
- /* If casting is the smaller (or equal) safety we match */
- return PyArray_MinCastSafety(safety, casting) == casting;
+ return is_valid;
}
@@ -610,28 +676,22 @@ can_cast_scalar_to(PyArray_Descr *scal_type, char *scal_data,
/*
* If the two dtypes are actually references to the same object
* or if casting type is forced unsafe then always OK.
+ *
+ * TODO: Assuming that unsafe casting always works is not actually correct
*/
if (scal_type == to || casting == NPY_UNSAFE_CASTING ) {
return 1;
}
- /* NOTE: This is roughly the same code as `PyArray_CanCastTypeTo`: */
- NPY_CASTING safety;
- if (PyDataType_ISUNSIZED(to) && to->subarray == NULL) {
- safety = PyArray_GetCastSafety(scal_type, NULL, NPY_DTYPE(to));
- }
- else {
- safety = PyArray_GetCastSafety(scal_type, to, NPY_DTYPE(to));
- }
- if (safety < 0) {
- PyErr_Clear();
- return 0;
- }
- safety = PyArray_MinCastSafety(safety, casting);
- if (safety == casting) {
+ int valid = PyArray_CheckCastSafety(casting, scal_type, to, NPY_DTYPE(to));
+ if (valid == 1) {
/* This is definitely a valid cast. */
return 1;
}
+ if (valid < 0) {
+ /* Probably must return 0, but just keep trying for now. */
+ PyErr_Clear();
+ }
/*
* If the scalar isn't a number, value-based casting cannot kick in and
@@ -692,14 +752,29 @@ PyArray_CanCastArrayTo(PyArrayObject *arr, PyArray_Descr *to,
NPY_CASTING casting)
{
PyArray_Descr *from = PyArray_DESCR(arr);
+ PyArray_DTypeMeta *to_dtype = NPY_DTYPE(to);
- /* If it's a scalar, check the value */
- if (PyArray_NDIM(arr) == 0 && !PyArray_HASFIELDS(arr)) {
+ /* NOTE, TODO: The same logic as `PyArray_CanCastTypeTo`: */
+ if (PyDataType_ISUNSIZED(to) && to->subarray == NULL) {
+ to = NULL;
+ }
+
+ /*
+ * If it's a scalar, check the value. (This only currently matters for
+ * numeric types and for `to == NULL` it can't be numeric.)
+ */
+ if (PyArray_NDIM(arr) == 0 && !PyArray_HASFIELDS(arr) && to != NULL) {
return can_cast_scalar_to(from, PyArray_DATA(arr), to, casting);
}
- /* Otherwise, use the standard rules */
- return PyArray_CanCastTypeTo(from, to, casting);
+ /* Otherwise, use the standard rules (same as `PyArray_CanCastTypeTo`) */
+ int is_valid = PyArray_CheckCastSafety(casting, from, to, to_dtype);
+ /* Clear any errors and consider this unsafe (should likely be changed) */
+ if (is_valid < 0) {
+ PyErr_Clear();
+ return 0;
+ }
+ return is_valid;
}
@@ -2122,13 +2197,6 @@ PyArray_AddCastingImplementation(PyBoundArrayMethodObject *meth)
meth->method->name);
return -1;
}
- if ((meth->method->casting & ~_NPY_CAST_IS_VIEW) != NPY_NO_CASTING) {
- PyErr_Format(PyExc_TypeError,
- "A cast where input and output DType (class) are identical "
- "must signal `no-casting`. (method: %s)",
- meth->method->name);
- return -1;
- }
if (meth->dtypes[0]->within_dtype_castingimpl != NULL) {
PyErr_Format(PyExc_RuntimeError,
"A cast was already added for %S -> %S. (method: %s)",
@@ -2400,7 +2468,7 @@ add_numeric_cast(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to)
/* Find the correct casting level, and special case no-cast */
if (dtypes[0]->kind == dtypes[1]->kind && from_itemsize == to_itemsize) {
- spec.casting = NPY_NO_CASTING;
+ spec.casting = NPY_EQUIV_CASTING;
/* When there is no casting (equivalent C-types) use byteswap loops */
slots[0].slot = NPY_METH_resolve_descriptors;
@@ -2558,7 +2626,6 @@ cast_to_string_resolve_descriptors(
dtypes[1]->type_num == NPY_STRING);
return NPY_UNSAFE_CASTING;
}
- assert(self->casting == NPY_SAFE_CASTING);
if (loop_descrs[1]->elsize >= size) {
return NPY_SAFE_CASTING;
@@ -2600,9 +2667,9 @@ add_other_to_and_from_string_cast(
.dtypes = dtypes,
.slots = slots,
};
- /* Almost everything can be safely cast to string (except unicode) */
+ /* Almost everything can be same-kind cast to string (except unicode) */
if (other->type_num != NPY_UNICODE) {
- spec.casting = NPY_SAFE_CASTING;
+ spec.casting = NPY_SAME_KIND_CASTING; /* same-kind if too short */
}
else {
spec.casting = NPY_UNSAFE_CASTING;
@@ -2722,7 +2789,7 @@ PyArray_InitializeStringCasts(void)
{0, NULL}};
PyArrayMethod_Spec spec = {
.name = "string_to_string_cast",
- .casting = NPY_NO_CASTING,
+ .casting = NPY_UNSAFE_CASTING,
.nin = 1,
.nout = 1,
.flags = (NPY_METH_REQUIRES_PYAPI |
@@ -2935,7 +3002,7 @@ PyArray_GetGenericToVoidCastingImpl(void)
method->name = "any_to_void_cast";
method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI;
- method->casting = NPY_SAFE_CASTING;
+ method->casting = -1;
method->resolve_descriptors = &nonstructured_to_structured_resolve_descriptors;
method->get_strided_loop = &nonstructured_to_structured_get_loop;
@@ -3074,7 +3141,7 @@ PyArray_GetVoidToGenericCastingImpl(void)
method->name = "void_to_any_cast";
method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI;
- method->casting = NPY_UNSAFE_CASTING;
+ method->casting = -1;
method->resolve_descriptors = &structured_to_nonstructured_resolve_descriptors;
method->get_strided_loop = &structured_to_nonstructured_get_loop;
@@ -3306,7 +3373,7 @@ PyArray_InitializeVoidToVoidCast(void)
{0, NULL}};
PyArrayMethod_Spec spec = {
.name = "void_to_void_cast",
- .casting = NPY_NO_CASTING,
+ .casting = -1, /* may not cast at all */
.nin = 1,
.nout = 1,
.flags = NPY_METH_REQUIRES_PYAPI | NPY_METH_SUPPORTS_UNALIGNED,
diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c
index fdf4c0839..48dc15340 100644
--- a/numpy/core/src/multiarray/datetime.c
+++ b/numpy/core/src/multiarray/datetime.c
@@ -3952,7 +3952,6 @@ time_to_string_resolve_descriptors(
return -1;
}
- assert(self->casting == NPY_UNSAFE_CASTING);
return NPY_UNSAFE_CASTING;
}
@@ -4059,7 +4058,7 @@ PyArray_InitializeDatetimeCasts()
.name = "datetime_casts",
.nin = 1,
.nout = 1,
- .casting = NPY_NO_CASTING,
+ .casting = NPY_UNSAFE_CASTING,
.flags = NPY_METH_SUPPORTS_UNALIGNED,
.slots = slots,
.dtypes = dtypes,
diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py
index 3bd01e5b4..0204d79c9 100644
--- a/numpy/core/tests/test_api.py
+++ b/numpy/core/tests/test_api.py
@@ -281,6 +281,19 @@ def test_array_astype():
a = np.array(1000, dtype='i4')
assert_raises(TypeError, a.astype, 'U1', casting='safe')
+@pytest.mark.parametrize("dt", ["S", "U"])
+def test_array_astype_to_string_discovery_empty(dt):
+ # See also gh-19085
+ arr = np.array([""], dtype=object)
+ # Note, the itemsize is the `0 -> 1` logic, which should change.
+ # The important part the test is rather that it does not error.
+ assert arr.astype(dt).dtype.itemsize == np.dtype(f"{dt}1").itemsize
+
+ # check the same thing for `np.can_cast` (since it accepts arrays)
+ assert np.can_cast(arr, dt, casting="unsafe")
+ assert not np.can_cast(arr, dt, casting="same_kind")
+ # as well as for the object as a descriptor:
+ assert np.can_cast("O", dt, casting="unsafe")
@pytest.mark.parametrize("dt", ["d", "f", "S13", "U32"])
def test_array_astype_to_void(dt):
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 8fd7ccf4e..70e4f1044 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -6947,6 +6947,13 @@ class TestNeighborhoodIter:
x, [-1, 0, -1, 1], 4, NEIGH_MODE['constant'])
assert_array_equal(l, r)
+ # Test with start in the middle
+ r = [np.array([[4, 0, 1], [4, 2, 3]], dtype=dt),
+ np.array([[0, 1, 4], [2, 3, 4]], dtype=dt)]
+ l = _multiarray_tests.test_neighborhood_iterator(
+ x, [-1, 0, -1, 1], 4, NEIGH_MODE['constant'], 2)
+ assert_array_equal(l, r)
+
def test_mirror2d(self, dt):
x = np.array([[0, 1], [2, 3]], dtype=dt)
r = [np.array([[0, 0, 1], [0, 0, 1]], dtype=dt),
diff --git a/numpy/lib/index_tricks.py b/numpy/lib/index_tricks.py
index 72d8e9de4..5140ffa61 100644
--- a/numpy/lib/index_tricks.py
+++ b/numpy/lib/index_tricks.py
@@ -631,7 +631,8 @@ class ndindex:
Examples
--------
- # dimensions as individual arguments
+ Dimensions as individual arguments
+
>>> for index in np.ndindex(3, 2, 1):
... print(index)
(0, 0, 0)
@@ -641,7 +642,8 @@ class ndindex:
(2, 0, 0)
(2, 1, 0)
- # same dimensions - but in a tuple (3, 2, 1)
+ Same dimensions - but in a tuple ``(3, 2, 1)``
+
>>> for index in np.ndindex((3, 2, 1)):
... print(index)
(0, 0, 0)