summaryrefslogtreecommitdiff
path: root/numpy/core
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2013-08-11 20:52:39 -0600
committerCharles Harris <charlesr.harris@gmail.com>2013-08-22 09:50:56 -0600
commitfd6cfd6828950850da27b09dab93a06dfe86308f (patch)
tree933b759ee252fc9224aa7d0704365b263a1db5f9 /numpy/core
parent9464075c7260475bdd5d693b3046379a2bb62482 (diff)
downloadnumpy-fd6cfd6828950850da27b09dab93a06dfe86308f.tar.gz
ENH: Make the ndarray diagonal method return a view.
Also remove the test_diagonal_deprecation test and add test that checks that a view is returned and that it is not writeable. Closes #596.
Diffstat (limited to 'numpy/core')
-rw-r--r--numpy/core/fromnumeric.py15
-rw-r--r--numpy/core/src/multiarray/item_selection.c15
-rw-r--r--numpy/core/tests/test_multiarray.py131
3 files changed, 28 insertions, 133 deletions
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py
index ca18d64ea..8d2c6ac95 100644
--- a/numpy/core/fromnumeric.py
+++ b/numpy/core/fromnumeric.py
@@ -1125,16 +1125,15 @@ def diagonal(a, offset=0, axis1=0, axis2=1):
In versions of NumPy prior to 1.7, this function always returned a new,
independent array containing a copy of the values in the diagonal.
- In NumPy 1.7, it continues to return a copy of the diagonal, but depending
- on this fact is deprecated. Writing to the resulting array continues to
- work as it used to, but a FutureWarning will be issued.
+ In NumPy 1.7 and 1.8, it continues to return a copy of the diagonal,
+ but depending on this fact is deprecated. Writing to the resulting
+ array continues to work as it used to, but a FutureWarning is issued.
- In NumPy 1.9, it will switch to returning a read-only view on the original
- array. Attempting to write to the resulting array will produce an error.
+ In NumPy 1.9 it returns a read-only view on the original array.
+ Attempting to write to the resulting array will produce an error.
- In NumPy 1.10, it will still return a view, but this view will no longer be
- marked read-only. Writing to the returned array will alter your original
- array as well.
+ In NumPy 1.10, it will return a read/write view, Writing to the returned
+ array will alter your original array.
If you don't write to the array returned by this function, then you can
just ignore all of the above.
diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c
index fe43872d8..81338ee32 100644
--- a/numpy/core/src/multiarray/item_selection.c
+++ b/numpy/core/src/multiarray/item_selection.c
@@ -2341,14 +2341,13 @@ PyArray_Diagonal(PyArrayObject *self, int offset, int axis1, int axis2)
return NULL;
}
- /* For backwards compatibility, during the deprecation period: */
- copy = PyArray_NewCopy(ret, NPY_KEEPORDER);
- Py_DECREF(ret);
- if (!copy) {
- return NULL;
- }
- PyArray_ENABLEFLAGS((PyArrayObject *)copy, NPY_ARRAY_WARN_ON_WRITE);
- return copy;
+ /*
+ * For numpy 1.9 the diagonal view is not writeable.
+ * This line needs to be removed in 1.10.
+ */
+ PyArray_CLEARFLAGS(ret, NPY_ARRAY_WRITEABLE);
+
+ return ret;
}
/*NUMPY_API
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 12d1e8ea9..9870d44f3 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -1343,123 +1343,20 @@ class TestMethods(TestCase):
# Order of axis argument doesn't matter:
assert_equal(b.diagonal(0, 2, 1), [[0, 3], [4, 7]])
- def test_diagonal_deprecation(self):
-
- def collect_warning_types(f, *args, **kwargs):
- with warnings.catch_warnings(record=True) as log:
- warnings.simplefilter("always")
- f(*args, **kwargs)
- return [w.category for w in log]
-
- a = np.arange(9).reshape(3, 3)
- # All the different functions raise a warning, but not an error, and
- # 'a' is not modified:
- assert_equal(collect_warning_types(a.diagonal().__setitem__, 0, 10),
- [FutureWarning])
- assert_equal(a, np.arange(9).reshape(3, 3))
- assert_equal(collect_warning_types(np.diagonal(a).__setitem__, 0, 10),
- [FutureWarning])
- assert_equal(a, np.arange(9).reshape(3, 3))
- assert_equal(collect_warning_types(np.diag(a).__setitem__, 0, 10),
- [FutureWarning])
- assert_equal(a, np.arange(9).reshape(3, 3))
- # Views also warn
- d = np.diagonal(a)
- d_view = d.view()
- assert_equal(collect_warning_types(d_view.__setitem__, 0, 10),
- [FutureWarning])
- # But the write goes through:
- assert_equal(d[0], 10)
- # Only one warning per call to diagonal, though (even if there are
- # multiple views involved):
- assert_equal(collect_warning_types(d.__setitem__, 0, 10),
- [])
-
- # Other ways of accessing the data also warn:
- # .data goes via the C buffer API, gives a read-write
- # buffer/memoryview. We don't warn until tp_getwritebuf is actually
- # called, which is not until the buffer is written to.
- have_memoryview = (hasattr(__builtins__, "memoryview")
- or "memoryview" in __builtins__)
- def get_data_and_write(getter):
- buf_or_memoryview = getter(a.diagonal())
- if (have_memoryview and isinstance(buf_or_memoryview, memoryview)):
- buf_or_memoryview[0] = np.array(1)
- else:
- buf_or_memoryview[0] = "x"
- assert_equal(collect_warning_types(get_data_and_write,
- lambda d: d.data),
- [FutureWarning])
- if hasattr(np, "getbuffer"):
- assert_equal(collect_warning_types(get_data_and_write,
- np.getbuffer),
- [FutureWarning])
- # PEP 3118:
- if have_memoryview:
- assert_equal(collect_warning_types(get_data_and_write, memoryview),
- [FutureWarning])
- # Void dtypes can give us a read-write buffer, but only in Python 2:
- import sys
- if sys.version_info[0] < 3:
- aV = np.empty((3, 3), dtype="V10")
- assert_equal(collect_warning_types(aV.diagonal().item, 0),
- [FutureWarning])
- # XX it seems that direct indexing of a void object returns a void
- # scalar, which ignores not just WARN_ON_WRITE but even WRITEABLE.
- # i.e. in this:
- # a = np.empty(10, dtype="V10")
- # a.flags.writeable = False
- # buf = a[0].item()
- # 'buf' ends up as a writeable buffer. I guess no-one actually
- # uses void types like this though...
- # __array_interface also lets a data pointer get away from us
- log = collect_warning_types(getattr, a.diagonal(),
- "__array_interface__")
- assert_equal(log, [FutureWarning])
- # ctypeslib goes via __array_interface__:
- try:
- # may not exist in python 2.4:
- import ctypes
- except ImportError:
- pass
- else:
- log = collect_warning_types(np.ctypeslib.as_ctypes, a.diagonal())
- assert_equal(log, [FutureWarning])
- # __array_struct__
- log = collect_warning_types(getattr, a.diagonal(), "__array_struct__")
- assert_equal(log, [FutureWarning])
-
- # Make sure that our recommendation to silence the warning by copying
- # the array actually works:
- diag_copy = a.diagonal().copy()
- assert_equal(collect_warning_types(diag_copy.__setitem__, 0, 10),
- [])
- # There might be people who get a spurious warning because they are
- # extracting a buffer, but then use that buffer in a read-only
- # fashion. And they might get cranky at having to create a superfluous
- # copy just to work around this spurious warning. A reasonable
- # solution would be for them to mark their usage as read-only, and
- # thus safe for both past and future PyArray_Diagonal
- # semantics. So let's make sure that setting the diagonal array to
- # non-writeable will suppress these warnings:
- ro_diag = a.diagonal()
- ro_diag.flags.writeable = False
- assert_equal(collect_warning_types(getattr, ro_diag, "data"), [])
- # __array_interface__ has no way to communicate read-onlyness --
- # effectively all __array_interface__ arrays are assumed to be
- # writeable :-(
- # ro_diag = a.diagonal()
- # ro_diag.flags.writeable = False
- # assert_equal(collect_warning_types(getattr, ro_diag,
- # "__array_interface__"), [])
- if hasattr(__builtins__, "memoryview"):
- ro_diag = a.diagonal()
- ro_diag.flags.writeable = False
- assert_equal(collect_warning_types(memoryview, ro_diag), [])
- ro_diag = a.diagonal()
- ro_diag.flags.writeable = False
- assert_equal(collect_warning_types(getattr, ro_diag,
- "__array_struct__"), [])
+ def test_diagonal_view_notwriteable(self):
+ # this test is only for 1.9, the diagonal view will be
+ # writeable in 1.10.
+ a = np.eye(3).diagonal()
+ assert_(not a.flags.writeable)
+ assert_(not a.flags.owndata)
+
+ a = np.diagonal(np.eye(3))
+ assert_(not a.flags.writeable)
+ assert_(not a.flags.owndata)
+
+ a = np.diag(np.eye(3))
+ assert_(not a.flags.writeable)
+ assert_(not a.flags.owndata)
def test_diagonal_memleak(self):
# Regression test for a bug that crept in at one point