8000 MAINT: Rewrite can-cast logic in terms of NEP 42 by seberg · Pull Request #17401 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

MAINT: Rewrite can-cast logic in terms of NEP 42 #17401

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

Merged
merged 5 commits into from
Nov 25, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Last touch-ups (test and tiny fixes)
Lets defer further touch ups to later... One more run, since the
last one errored (hopefully due to an old failure not merged
correctly)
  • Loading branch information
seberg committed Nov 25, 2020
commit 39d2e8b2d81ca0989ab32bfbd234b2cb8dd7cce7
4 changes: 2 additions & 2 deletions numpy/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
NPY_RELAXED_STRIDES_DEBUG = NPY_RELAXED_STRIDES_DEBUG and NPY_RELAXED_STRIDES_CHECKING

# Set to True to use the new casting implementation as much as implemented.
# Allows running the full test suit to excercise the new machinery until
# Allows running the full test suit to exercise the new machinery until
# it is used as default and the old version is eventually deleted.
NPY_USE_NEW_CASTINGIMPL = os.environ.get('NPY_USE_NEW_CASTINGIMPL', "0") != 0
NPY_USE_NEW_CASTINGIMPL = os.environ.get('NPY_USE_NEW_CASTINGIMPL', "1") != "0"

# XXX: ugly, we use a class to avoid calling twice some expensive functions in
# config.h/numpyconfig.h. I don't see a better way because distutils force
Expand Down
23 changes: 11 additions & 12 deletions numpy/core/src/multiarray/convert_datatype.c
Original file line number Diff line number Diff line change
Expand Up @@ -420,20 +420,20 @@ PyArray_CanCastSafely(int fromtype, int totype)
#if NPY_USE_NEW_CASTINGIMPL
PyArray_DTypeMeta *from = PyArray_DTypeFromTypeNum(fromtype);
if (from == NULL) {
PyErr_WriteUnraisable(Py_None);
PyErr_WriteUnraisable(NULL);
return 0;
}
PyArray_DTypeMeta *to = PyArray_DTypeFromTypeNum(totype);
if (to == NULL) {
PyErr_WriteUnraisable(Py_None);
PyErr_WriteUnraisable(NULL);
return 0;
}
PyObject *castingimpl = PyArray_GetCastingImpl(from, to);
Py_DECREF(from);
Py_DECREF(to);

if (castingimpl == NULL) {
PyErr_WriteUnraisable(Py_None);
PyErr_WriteUnraisable(NULL);
return 0;
}
else if (castingimpl == Py_None) {
Expand Down Expand Up @@ -2489,9 +2489,9 @@ structured_to_nonstructured_resolve_descriptors(
if (given_descrs[1] == NULL) {
loop_descrs[1] = dtypes[1]->default_descr(dtypes[1]);
/*
* Special case strings here, this is probably unnecessary and
* should be useless (i.e. it is necessary to use empty arrays to
* trigger this path.).
* Special case strings here, it should be useless (and only actually
* work for empty arrays). Possibly this should simply raise for
* all parametric DTypes.
*/
if (dtypes[1]->type_num == NPY_STRING) {
loop_descrs[1]->elsize = given_descrs[0]->elsize;
Expand Down Expand Up @@ -2744,7 +2744,7 @@ object_to_any_resolve_descriptors(
* here is that e.g. "M8" input is considered to be the DType class,
* and by allowing it here, we go back to the "M8" instance.
*/
if (dtypes[1]->parametric && !dtypes[1]->legacy) {
if (dtypes[1]->parametric) {
PyErr_Format(PyExc_TypeError,
"casting from object to the parametric DType %S requires "
"the specified output dtype instance. "
Expand All @@ -2756,11 +2756,6 @@ object_to_any_resolve_descriptors(
if (loop_descrs[1] == NULL) {
return -1;
}
if (dtypes[1]->type_num == NPY_VOID) {
/* NOTE: This appears to be behaviour as of 1.19 (void is not
* adjusted) */
loop_descrs[1]->elsize = sizeof(PyObject *);
}
}
else {
Py_INCREF(given_descrs[1]);
Expand Down Expand Up @@ -2791,6 +2786,8 @@ PyArray_GetObjectToGenericCastingImpl()
return PyErr_NoMemory();
}

method->nin = 1;
method->nout = 1;
method->name = "object_to_any_cast";
method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI;
method->casting = NPY_UNSAFE_CASTING;
Expand Down Expand Up @@ -2845,6 +2842,8 @@ PyArray_GetGenericToObjectCastingImpl()
return PyErr_NoMemory();
}

method->nin = 1;
method->nout = 1;
method->name = "any_to_object_cast";
method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI;
method->casting = NPY_SAFE_CASTING;
Expand Down
17 changes: 17 additions & 0 deletions numpy/core/tests/test_casting_unittests.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,20 @@ def test_string_to_string_cancast(self, other_dt, string_char):
assert safety == expected_safety
elif change_length > 0:
assert safety == Casting.safe

def test_void_to_string_special_case(self):
# Cover a small special case in void to string casting that could
# probably just as well be turned into an error (compare
# `test_object_to_parametric_internal_error` below).
assert np.array([], dtype="V5").astype("S").dtype.itemsize == 5
assert np.array([], dtype="V5").astype("U").dtype.itemsize == 4 * 5

def test_object_to_parametric_internal_error(self):
# We reject casting from object to a parametric type, without
# figuring out the correct instance first.
object_dtype = type(np.dtype(object))
other_dtype = type(np.dtype(str))
cast = get_castingimpl(object_dtype, other_dtype)
with pytest.raises(TypeError,
match="casting from object to the parametric DType"):
cast._resolve_descriptors((np.dtype("O"), None))
0