Skip to content

Commit a914726

Browse files
committed
MAINT: refactor to pass casting safety in
1 parent b7f6de9 commit a914726

File tree

5 files changed

+36
-67
lines changed

5 files changed

+36
-67
lines changed

numpy/_core/src/multiarray/ctors.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1908,11 +1908,9 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags)
19081908
}
19091909

19101910
arrflags = PyArray_FLAGS(arr);
1911-
unsigned char viewable = PyArray_ViewableTypes(oldtype, newtype);
1912-
if (viewable < 0) {
1913-
Py_DECREF(newtype);
1914-
return NULL;
1915-
}
1911+
npy_intp view_offset;
1912+
npy_intp is_safe = PyArray_SafeCast(oldtype, newtype, &view_offset, NPY_NO_CASTING, 1);
1913+
npy_intp view_safe = (is_safe && (view_offset == 0));
19161914

19171915
/* If a guaranteed copy was requested */
19181916
copy = (flags & NPY_ARRAY_ENSURECOPY) ||
@@ -1928,7 +1926,7 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags)
19281926
/* If a writeable array was requested, and arr is not */
19291927
((flags & NPY_ARRAY_WRITEABLE) &&
19301928
(!(arrflags & NPY_ARRAY_WRITEABLE))) ||
1931-
!viewable;
1929+
!view_safe;
19321930

19331931
if (copy) {
19341932
if (flags & NPY_ARRAY_ENSURENOCOPY ) {

numpy/_core/src/multiarray/multiarraymodule.c

Lines changed: 19 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,23 +1449,6 @@ PyArray_EquivTypes(PyArray_Descr *type1, PyArray_Descr *type2)
14491449
return 1;
14501450
}
14511451

1452-
if (Py_TYPE(Py_TYPE(type1)) == &PyType_Type) {
1453-
/*
1454-
* 2021-12-17: This case is nonsense and should be removed eventually!
1455-
*
1456-
* boost::python has/had a bug effectively using EquivTypes with
1457-
* `type(arbitrary_obj)`. That is clearly wrong as that cannot be a
1458-
* `PyArray_Descr *`. We assume that `type(type(type(arbitrary_obj))`
1459-
* is always in practice `type` (this is the type of the metaclass),
1460-
* but for our descriptors, `type(type(descr))` is DTypeMeta.
1461-
*
1462-
* In that case, we just return False. There is a possibility that
1463-
* this actually _worked_ effectively (returning 1 sometimes).
1464-
* We ignore that possibility for simplicity; it really is not our bug.
1465-
*/
1466-
return 0;
1467-
}
1468-
14691452
/*
14701453
* Do not use PyArray_CanCastTypeTo because it supports legacy flexible
14711454
* dtypes as input.
@@ -1482,45 +1465,29 @@ PyArray_EquivTypes(PyArray_Descr *type1, PyArray_Descr *type2)
14821465

14831466

14841467
/*
1485-
* This function returns true if a view can be safely created
1486-
* between the two types. This implies that PyArray_EquivTypes
1487-
* is true as well.
1468+
* This function returns true if the two types can be safely cast at
1469+
* *minimum_safety* casting level. Sets the view_offset if that is set
1470+
* for the cast. If ignore_error is 1, errors in cast setup are ignored.
14881471
*/
1489-
NPY_NO_EXPORT unsigned char
1490-
PyArray_ViewableTypes(PyArray_Descr *type1, PyArray_Descr *type2)
1472+
NPY_NO_EXPORT npy_intp
1473+
PyArray_SafeCast(PyArray_Descr *type1, PyArray_Descr *type2,
1474+
npy_intp* view_offset, NPY_CASTING minimum_safety,
1475+
npy_intp ignore_error)
14911476
{
14921477
if (type1 == type2) {
1478+
*view_offset = 0;
14931479
return 1;
14941480
}
14951481

1496-
if (Py_TYPE(Py_TYPE(type1)) == &PyType_Type) {
1497-
/*
1498-
* 2021-12-17: This case is nonsense and should be removed eventually!
1499-
*
1500-
* boost::python has/had a bug effectively using EquivTypes with
1501-
* `type(arbitrary_obj)`. That is clearly wrong as that cannot be a
1502-
* `PyArray_Descr *`. We assume that `type(type(type(arbitrary_obj))`
1503-
* is always in practice `type` (this is the type of the metaclass),
1504-
* but for our descriptors, `type(type(descr))` is DTypeMeta.
1505-
*
1506-
* In that case, we just return False. There is a possibility that
1507-
* this actually _worked_ effectively (returning 1 sometimes).
1508-
* We ignore that possibility for simplicity; it really is not our bug.
1509-
*/
1510-
return 0;
1511-
}
1512-
1513-
npy_intp view_offset;
1514-
NPY_CASTING safety = PyArray_GetCastInfo(type1, type2, NULL, &view_offset);
1482+
NPY_CASTING safety = PyArray_GetCastInfo(type1, type2, NULL, view_offset);
15151483
if (safety < 0) {
1516-
PyErr_Clear();
1517-
return 0;
1518-
}
1519-
if (view_offset != 0) {
1520-
return 0;
1484+
if (ignore_error) {
1485+
PyErr_Clear();
1486+
return 0;
1487+
}
1488+
return -1;
15211489
}
1522-
/* If casting is "no casting" this dtypes are considered equivalent. */
1523-
return PyArray_MinCastSafety(safety, NPY_NO_CASTING) == NPY_NO_CASTING;
1490+
return PyArray_MinCastSafety(safety, minimum_safety) == minimum_safety;
15241491
}
15251492

15261493

@@ -1652,11 +1619,10 @@ _array_fromobject_generic(
16521619

16531620
/* One more chance for faster exit if user specified the dtype. */
16541621
oldtype = PyArray_DESCR(oparr);
1655-
unsigned char viewable = PyArray_ViewableTypes(oldtype, dtype);
1656-
if (viewable < 0) {
1657-
goto finish;
1658-
}
1659-
if (viewable) {
1622+
npy_intp view_offset;
1623+
npy_intp is_safe = PyArray_SafeCast(oldtype, dtype, &view_offset, NPY_NO_CASTING, 1);
1624+
npy_intp view_safe = (is_safe && (view_offset == 0));
1625+
if (view_safe) {
16601626
if (copy != NPY_COPY_ALWAYS && STRIDING_OK(oparr, order)) {
16611627
if (oldtype == dtype) {
16621628
Py_INCREF(op);

numpy/_core/src/multiarray/multiarraymodule.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_convert_if_no_array;
2121
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_cpu;
2222
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_err_msg_substr;
2323

24-
NPY_NO_EXPORT unsigned char
25-
PyArray_ViewableTypes(PyArray_Descr *type1, PyArray_Descr *type2);
24+
NPY_NO_EXPORT npy_intp
25+
PyArray_SafeCast(PyArray_Descr *type1, PyArray_Descr *type2,
26+
npy_intp* view_offset, NPY_CASTING minimum_safety,
27+
npy_intp ignore_errors);
2628

2729
#endif /* NUMPY_CORE_SRC_MULTIARRAY_MULTIARRAYMODULE_H_ */

numpy/_core/src/multiarray/stringdtype/casts.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ string_to_string_resolve_descriptors(PyObject *NPY_UNUSED(self),
8080
}
8181

8282
// views are only legal between descriptors that share allocators (e.g. the same object)
83-
*view_offset = descr0->allocator != descr1->allocator;
83+
if (descr0->allocator == descr1->allocator) {
84+
*view_offset = 0;
85+
};
8486

8587
return NPY_NO_CASTING;
8688
}

numpy/_core/src/umath/ufunc_object.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include "legacy_array_method.h"
6363
#include "abstractdtypes.h"
6464
#include "mapping.h"
65+
#include "multiarraymodule.h"
6566

6667
/* TODO: Only for `NpyIter_GetTransferFlags` until it is public */
6768
#define NPY_ITERATOR_IMPLEMENTATION_CODE
@@ -789,9 +790,9 @@ check_for_trivial_loop(PyArrayMethodObject *ufuncimpl,
789790

790791
if (dtypes[i] != PyArray_DESCR(op[i])) {
791792
npy_intp view_offset;
792-
NPY_CASTING safety = PyArray_GetCastInfo(
793-
PyArray_DESCR(op[i]), dtypes[i], NULL, &view_offset);
794-
if (safety < 0 && PyErr_Occurred()) {
793+
npy_intp is_safe = PyArray_SafeCast(
794+
PyArray_DESCR(op[i]), dtypes[i], &view_offset, casting, 0);
795+
if (is_safe < 0 && PyErr_Occurred()) {
795796
/* A proper error during a cast check, should be rare */
796797
return -1;
797798
}
@@ -806,8 +807,8 @@ check_for_trivial_loop(PyArrayMethodObject *ufuncimpl,
806807
* can force cast to bool)
807808
*/
808809
}
809-
else if (PyArray_MinCastSafety(safety, casting) != casting) {
810-
return 0; /* the cast is not safe enough */
810+
else if (is_safe != 1) {
811+
return 0; /* there was a cast error or cast is not safe enough */
811812
}
812813
}
813814
if (must_copy) {

0 commit comments

Comments
 (0)