summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorMarten van Kerkwijk <mhvk@astro.utoronto.ca>2022-01-07 10:24:49 -0500
committerMarten van Kerkwijk <mhvk@astro.utoronto.ca>2022-01-08 17:44:29 -0500
commitb375f4eace5636d87fb6e09de23e8b2cb5259a1e (patch)
treed9b089b5f01d94f65da917e7b05bcf27d0173497 /numpy
parentf4a3e07877eb258f69a7d65d8a3b7e8d96e1aacd (diff)
downloadnumpy-b375f4eace5636d87fb6e09de23e8b2cb5259a1e.tar.gz
ENH: Let ndarray.__array_finalize__ be callable.
This helps subclasses, who can now do super() in their own implementation.
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/_add_newdocs.py16
-rw-r--r--numpy/core/src/multiarray/getset.c13
-rw-r--r--numpy/core/src/multiarray/methods.c14
-rw-r--r--numpy/core/tests/test_multiarray.py10
-rw-r--r--numpy/ma/tests/test_subclassing.py5
5 files changed, 33 insertions, 25 deletions
diff --git a/numpy/core/_add_newdocs.py b/numpy/core/_add_newdocs.py
index a8d73af3f..219383e1e 100644
--- a/numpy/core/_add_newdocs.py
+++ b/numpy/core/_add_newdocs.py
@@ -2265,10 +2265,6 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_interface__',
"""Array protocol: Python side."""))
-add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_finalize__',
- """None."""))
-
-
add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_priority__',
"""Array priority."""))
@@ -2278,12 +2274,12 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_struct__',
add_newdoc('numpy.core.multiarray', 'ndarray', ('__dlpack__',
"""a.__dlpack__(*, stream=None)
-
+
DLPack Protocol: Part of the Array API."""))
add_newdoc('numpy.core.multiarray', 'ndarray', ('__dlpack_device__',
"""a.__dlpack_device__()
-
+
DLPack Protocol: Part of the Array API."""))
add_newdoc('numpy.core.multiarray', 'ndarray', ('base',
@@ -2811,6 +2807,14 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('__array__',
"""))
+add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_finalize__',
+ """a.__array_finalize__(obj, /)
+
+ Present so subclasses can call super. Does nothing.
+
+ """))
+
+
add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_prepare__',
"""a.__array_prepare__(array[, context], /)
diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c
index ac6465acd..a4f972ba4 100644
--- a/numpy/core/src/multiarray/getset.c
+++ b/numpy/core/src/multiarray/getset.c
@@ -926,15 +926,6 @@ array_transpose_get(PyArrayObject *self, void *NPY_UNUSED(ignored))
return PyArray_Transpose(self, NULL);
}
-/* If this is None, no function call is made
- --- default sub-class behavior
-*/
-static PyObject *
-array_finalize_get(PyArrayObject *NPY_UNUSED(self), void *NPY_UNUSED(ignored))
-{
- Py_RETURN_NONE;
-}
-
NPY_NO_EXPORT PyGetSetDef array_getsetlist[] = {
{"ndim",
(getter)array_ndim_get,
@@ -1008,10 +999,6 @@ NPY_NO_EXPORT PyGetSetDef array_getsetlist[] = {
(getter)array_priority_get,
NULL,
NULL, NULL},
- {"__array_finalize__",
- (getter)array_finalize_get,
- NULL,
- NULL, NULL},
{NULL, NULL, NULL, NULL, NULL}, /* Sentinel */
};
diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c
index 8dd7c112f..33f78dff2 100644
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -859,7 +859,7 @@ array_astype(PyArrayObject *self,
* and it's not a subtype if subok is False, then we
* can skip the copy.
*/
- if (forcecopy != NPY_COPY_ALWAYS &&
+ if (forcecopy != NPY_COPY_ALWAYS &&
(order == NPY_KEEPORDER ||
(order == NPY_ANYORDER &&
(PyArray_IS_C_CONTIGUOUS(self) ||
@@ -881,7 +881,7 @@ array_astype(PyArrayObject *self,
Py_DECREF(dtype);
return NULL;
}
-
+
if (!PyArray_CanCastArrayTo(self, dtype, casting)) {
PyErr_Clear();
npy_set_invalid_cast_error(
@@ -926,6 +926,13 @@ array_astype(PyArrayObject *self,
static PyObject *
+array_finalizearray(PyArrayObject *self, PyObject *obj)
+{
+ Py_RETURN_NONE;
+}
+
+
+static PyObject *
array_wraparray(PyArrayObject *self, PyObject *args)
{
PyArrayObject *arr;
@@ -2777,6 +2784,9 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
{"__array_prepare__",
(PyCFunction)array_preparearray,
METH_VARARGS, NULL},
+ {"__array_finalize__",
+ (PyCFunction)array_finalizearray,
+ METH_O, NULL},
{"__array_wrap__",
(PyCFunction)array_wraparray,
METH_VARARGS, NULL},
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 2529705d5..d8a8c4da4 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -8997,6 +8997,14 @@ class TestArrayFinalize:
break_cycles()
assert_(obj_ref() is None, "no references should remain")
+ def test_can_use_super(self):
+ class SuperFinalize(np.ndarray):
+ def __array_finalize__(self, obj):
+ self.saved_result = super().__array_finalize__(obj)
+
+ a = np.array(1).view(SuperFinalize)
+ assert_(a.saved_result is None)
+
def test_orderconverter_with_nonASCII_unicode_ordering():
# gh-7475
@@ -9201,7 +9209,7 @@ class TestViewDtype:
# x is non-contiguous
x = np.arange(10, dtype='<i4')[::2]
with pytest.raises(ValueError,
- match='the last axis must be contiguous'):
+ match='the last axis must be contiguous'):
x.view('<i2')
expected = [[0, 0], [2, 0], [4, 0], [6, 0], [8, 0]]
assert_array_equal(x[:, np.newaxis].view('<i2'), expected)
diff --git a/numpy/ma/tests/test_subclassing.py b/numpy/ma/tests/test_subclassing.py
index 83a9b2f51..3491cef7f 100644
--- a/numpy/ma/tests/test_subclassing.py
+++ b/numpy/ma/tests/test_subclassing.py
@@ -28,8 +28,7 @@ class SubArray(np.ndarray):
return x
def __array_finalize__(self, obj):
- if callable(getattr(super(), '__array_finalize__', None)):
- super().__array_finalize__(obj)
+ super().__array_finalize__(obj)
self.info = getattr(obj, 'info', {}).copy()
return
@@ -315,7 +314,7 @@ class TestSubclassing:
assert_startswith(repr(mx), 'masked_array')
xsub = SubArray(x)
mxsub = masked_array(xsub, mask=[True, False, True, False, False])
- assert_startswith(repr(mxsub),
+ assert_startswith(repr(mxsub),
f'masked_{SubArray.__name__}(data=[--, 1, --, 3, 4]')
def test_subclass_str(self):