summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c11
-rw-r--r--numpy/core/src/umath/ufunc_object.c50
-rw-r--r--numpy/lib/format.py2
3 files changed, 41 insertions, 22 deletions
diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c
index cd0e21098..067c356ca 100644
--- a/numpy/core/src/multiarray/convert_datatype.c
+++ b/numpy/core/src/multiarray/convert_datatype.c
@@ -391,7 +391,16 @@ _get_cast_safety_from_castingimpl(PyArrayMethodObject *castingimpl,
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) */
+ /*
+ * Check for less harmful non-standard returns. The following two returns
+ * should never happen. They would be roughly equivalent, but less precise,
+ * versions of `(NPY_NO_CASTING|_NPY_CAST_IS_VIEW)`.
+ * 1. No-casting must imply cast-is-view.
+ * 2. Equivalent-casting + cast-is-view is (currently) the definition
+ * of a "no" cast (there may be reasons to relax this).
+ * Note that e.g. `(NPY_UNSAFE_CASTING|_NPY_CAST_IS_VIEW)` is valid.
+ */
+ assert(casting != NPY_NO_CASTING);
assert(casting != (NPY_EQUIV_CASTING|_NPY_CAST_IS_VIEW));
return casting;
}
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index a862b69c1..1b47e74ac 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -49,6 +49,7 @@
#include "common.h"
#include "dtypemeta.h"
#include "numpyos.h"
+#include "convert_datatype.h"
/********** PRINTF DEBUG TRACING **************/
#define NPY_UF_DBG_TRACING 0
@@ -991,33 +992,44 @@ fail:
*/
static int
check_for_trivial_loop(PyUFuncObject *ufunc,
- PyArrayObject **op,
- PyArray_Descr **dtype,
- npy_intp buffersize)
+ PyArrayObject **op, PyArray_Descr **dtypes,
+ npy_intp buffersize)
{
- npy_intp i, nin = ufunc->nin, nop = nin + ufunc->nout;
+ int i, nin = ufunc->nin, nop = nin + ufunc->nout;
for (i = 0; i < nop; ++i) {
/*
* If the dtype doesn't match, or the array isn't aligned,
* indicate that the trivial loop can't be done.
*/
- if (op[i] != NULL &&
- (!PyArray_ISALIGNED(op[i]) ||
- !PyArray_EquivTypes(dtype[i], PyArray_DESCR(op[i]))
- )) {
+ if (op[i] == NULL) {
+ continue;
+ }
+ int must_copy = !PyArray_ISALIGNED(op[i]);
+
+ if (dtypes[i] != PyArray_DESCR(op[i])) {
+ NPY_CASTING safety = PyArray_GetCastSafety(
+ PyArray_DESCR(op[i]), dtypes[i], NULL);
+ if (safety < 0) {
+ /* A proper error during a cast check should be rare */
+ return -1;
+ }
+ if (!(safety & _NPY_CAST_IS_VIEW)) {
+ must_copy = 1;
+ }
+ }
+ if (must_copy) {
/*
* If op[j] is a scalar or small one dimensional
* array input, make a copy to keep the opportunity
- * for a trivial loop.
+ * for a trivial loop. Outputs are not copied here.
*/
- if (i < nin && (PyArray_NDIM(op[i]) == 0 ||
- (PyArray_NDIM(op[i]) == 1 &&
- PyArray_DIM(op[i],0) <= buffersize))) {
+ if (i < nin && (PyArray_NDIM(op[i]) == 0
+ || (PyArray_NDIM(op[i]) == 1
+ && PyArray_DIM(op[i], 0) <= buffersize))) {
PyArrayObject *tmp;
- Py_INCREF(dtype[i]);
- tmp = (PyArrayObject *)
- PyArray_CastToType(op[i], dtype[i], 0);
+ Py_INCREF(dtypes[i]);
+ tmp = (PyArrayObject *)PyArray_CastToType(op[i], dtypes[i], 0);
if (tmp == NULL) {
return -1;
}
@@ -2476,8 +2488,6 @@ PyUFunc_GenericFunctionInternal(PyUFuncObject *ufunc,
/* These parameters come from extobj= or from a TLS global */
int buffersize = 0, errormask = 0;
- int trivial_loop_ok = 0;
-
NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s\n", ufunc_name);
/* Get the buffersize and errormask */
@@ -2532,16 +2542,16 @@ PyUFunc_GenericFunctionInternal(PyUFuncObject *ufunc,
* Since it requires dtypes, it can only be called after
* ufunc->type_resolver
*/
- trivial_loop_ok = check_for_trivial_loop(ufunc,
+ int trivial_ok = check_for_trivial_loop(ufunc,
op, operation_descrs, buffersize);
- if (trivial_loop_ok < 0) {
+ if (trivial_ok < 0) {
return -1;
}
/* check_for_trivial_loop on half-floats can overflow */
npy_clear_floatstatus_barrier((char*)&ufunc);
- retval = execute_legacy_ufunc_loop(ufunc, trivial_loop_ok,
+ retval = execute_legacy_ufunc_loop(ufunc, trivial_ok,
op, operation_descrs, order,
buffersize, output_array_prepare,
full_args, op_flags);
diff --git a/numpy/lib/format.py b/numpy/lib/format.py
index ead6a0420..9b8f90dbd 100644
--- a/numpy/lib/format.py
+++ b/numpy/lib/format.py
@@ -44,7 +44,7 @@ Capabilities
read most ``.npy`` files that they have been given without much
documentation.
-- Allows memory-mapping of the data. See `open_memmep`.
+- Allows memory-mapping of the data. See `open_memmap`.
- Can be read from a filelike stream object instead of an actual file.