summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2018-12-16 16:00:53 -0800
committerGitHub <noreply@github.com>2018-12-16 16:00:53 -0800
commit059196b81e56bac83e1874310027c97e48fdf89a (patch)
tree726f8dd953306f2e42b61eb0e761e155c0a62887
parent7ce7382d5eacbc3da026289483a841184a609928 (diff)
parent46445c297930213ea141ce84010930c3f7a6c8a5 (diff)
downloadnumpy-059196b81e56bac83e1874310027c97e48fdf89a.tar.gz
Merge pull request #12571 from charris/revert-11721
Revert "Merge pull request #11721 from eric-wieser/fix-9647"
-rw-r--r--doc/release/1.16.0-notes.rst5
-rw-r--r--numpy/core/_add_newdocs.py8
-rw-r--r--numpy/core/_internal.py73
-rw-r--r--numpy/core/tests/test_multiarray.py40
-rw-r--r--numpy/ma/tests/test_core.py4
5 files changed, 25 insertions, 105 deletions
diff --git a/doc/release/1.16.0-notes.rst b/doc/release/1.16.0-notes.rst
index facce6e00..8d176c3ea 100644
--- a/doc/release/1.16.0-notes.rst
+++ b/doc/release/1.16.0-notes.rst
@@ -326,11 +326,6 @@ copying the data directly into the appropriate slice of the resulting array.
This results in significant speedups for these large arrays, particularly for
arrays being blocked along more than 2 dimensions.
-``arr.ctypes.data_as(...)`` holds a reference to arr
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Previously the caller was responsible for keeping the array alive for the
-lifetime of the pointer.
-
Speedup ``np.take`` for read-only arrays
----------------------------------------
The implementation of ``np.take`` no longer makes an unnecessary copy of the
diff --git a/numpy/core/_add_newdocs.py b/numpy/core/_add_newdocs.py
index 513415e09..80fa1466e 100644
--- a/numpy/core/_add_newdocs.py
+++ b/numpy/core/_add_newdocs.py
@@ -2042,6 +2042,14 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('ctypes',
.. automethod:: numpy.core._internal._ctypes.strides_as
+ Be careful using the ctypes attribute - especially on temporary
+ arrays or arrays constructed on the fly. For example, calling
+ ``(a+b).ctypes.data_as(ctypes.c_void_p)`` returns a pointer to memory
+ that is invalid because the array created as (a+b) is deallocated
+ before the next Python statement. You can avoid this problem using
+ either ``c=a+b`` or ``ct=(a+b).ctypes``. In the latter case, ct will
+ hold a reference to the array until ct is deleted or re-assigned.
+
If the ctypes module is not available, then the ctypes attribute
of array objects still returns something useful, but ctypes objects
are not returned and errors may be raised instead. In particular,
diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py
index a38a7dc35..59da60253 100644
--- a/numpy/core/_internal.py
+++ b/numpy/core/_internal.py
@@ -238,45 +238,19 @@ _getintp_ctype.cache = None
class _missing_ctypes(object):
def cast(self, num, obj):
- return obj.value
-
- class c_void_p(object):
- def __init__(self, ptr):
- self.value = ptr
-
-
-
-def _get_void_ptr(arr):
- """
- Get a `ctypes.c_void_p` to arr.data, that keeps a reference to the array
- """
- import numpy as np
- # don't let subclasses interfere
- arr = arr.view(ndarray)
- # collapse the array to point to at most 1 element, so it become contiguous
- arr = arr[np.s_[:1,] * arr.ndim + np.s_[...,]]
- # then convert to ctypes now that we've reduced it to a simple, empty, array
- arr.flags.writeable = True
- arr = (ctypes.c_char * 0).from_buffer(arr)
- # finally cast to void*
- return ctypes.cast(ctypes.pointer(arr), ctypes.c_void_p)
+ return num
+ def c_void_p(self, num):
+ return num
class _ctypes(object):
def __init__(self, array, ptr=None):
- self._arr = array
-
if ctypes:
self._ctypes = ctypes
- # get a void pointer to the buffer, which keeps the array alive
- self._data = _get_void_ptr(array)
- assert self._data.value == ptr
else:
- # fake a pointer-like object that holds onto the reference
self._ctypes = _missing_ctypes()
- self._data = self._ctypes.c_void_p(ptr)
- self._data._objects = array
-
+ self._arr = array
+ self._data = ptr
if self._arr.ndim == 0:
self._zerod = True
else:
@@ -289,8 +263,6 @@ class _ctypes(object):
``self.data_as(ctypes.c_void_p)``. Perhaps you want to use the data as a
pointer to a ctypes array of floating-point data:
``self.data_as(ctypes.POINTER(ctypes.c_double))``.
-
- The returned pointer will keep a reference to the array.
"""
return self._ctypes.cast(self._data, obj)
@@ -312,8 +284,7 @@ class _ctypes(object):
return None
return (obj*self._arr.ndim)(*self._arr.strides)
- @property
- def data(self):
+ def get_data(self):
"""
A pointer to the memory area of the array as a Python integer.
This memory area may contain data that is not aligned, or not in correct
@@ -322,16 +293,10 @@ class _ctypes(object):
attribute to arbitrary C-code to avoid trouble that can include Python
crashing. User Beware! The value of this attribute is exactly the same
as ``self._array_interface_['data'][0]``.
-
- Note that unlike `data_as`, a reference will not be kept to the array:
- code like ``ctypes.c_void_p((a + b).ctypes.data)`` will result in a
- pointer to a deallocated array, and should be spelt
- ``(a + b).ctypes.data_as(ctypes.c_void_p)``
"""
- return self._data.value
+ return self._data
- @property
- def shape(self):
+ def get_shape(self):
"""
(c_intp*self.ndim): A ctypes array of length self.ndim where
the basetype is the C-integer corresponding to ``dtype('p')`` on this
@@ -342,8 +307,7 @@ class _ctypes(object):
"""
return self.shape_as(_getintp_ctype())
- @property
- def strides(self):
+ def get_strides(self):
"""
(c_intp*self.ndim): A ctypes array of length self.ndim where
the basetype is the same as for the shape attribute. This ctypes array
@@ -353,20 +317,13 @@ class _ctypes(object):
"""
return self.strides_as(_getintp_ctype())
- @property
- def _as_parameter_(self):
- """
- Overrides the ctypes semi-magic method
-
- Enables `c_func(some_array.ctypes)`
- """
- return self._data
+ def get_as_parameter(self):
+ return self._ctypes.c_void_p(self._data)
- # kept for compatibility
- get_data = data.fget
- get_shape = shape.fget
- get_strides = strides.fget
- get_as_parameter = _as_parameter_.fget
+ data = property(get_data)
+ shape = property(get_shape)
+ strides = property(get_strides)
+ _as_parameter_ = property(get_as_parameter, None, doc="_as parameter_")
def _newnames(datatype, order):
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 124629c6a..32d49df02 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -7626,46 +7626,6 @@ class TestCTypes(object):
finally:
_internal.ctypes = ctypes
- def _make_readonly(x):
- x.flags.writeable = False
- return x
-
- @pytest.mark.parametrize('arr', [
- np.array([1, 2, 3]),
- np.array([['one', 'two'], ['three', 'four']]),
- np.array((1, 2), dtype='i4,i4'),
- np.array([None], dtype=object),
- np.array([]),
- np.empty((0, 0)),
- _make_readonly(np.array([1, 2, 3])),
- ], ids=[
- '1d',
- '2d',
- 'structured',
- 'object',
- 'empty',
- 'empty-2d',
- 'readonly'
- ])
- def test_ctypes_data_as_holds_reference(self, arr):
- # gh-9647
- # create a copy to ensure that pytest does not mess with the refcounts
- arr = arr.copy()
-
- arr_ref = weakref.ref(arr)
-
- ctypes_ptr = arr.ctypes.data_as(ctypes.c_void_p)
-
- # `ctypes_ptr` should hold onto `arr`
- del arr
- gc.collect()
- assert_(arr_ref() is not None, "ctypes pointer did not hold onto a reference")
-
- # but when the `ctypes_ptr` object dies, so should `arr`
- del ctypes_ptr
- gc.collect()
- assert_(arr_ref() is None, "unknowable whether ctypes pointer holds a reference")
-
class TestWritebackIfCopy(object):
# all these tests use the WRITEBACKIFCOPY mechanism
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
index e0dbf1b1a..2775b11ec 100644
--- a/numpy/ma/tests/test_core.py
+++ b/numpy/ma/tests/test_core.py
@@ -2401,9 +2401,9 @@ class TestMaskedArrayInPlaceArithmetics(object):
assert_equal(xm, y + 1)
(x, _, xm) = self.floatdata
- id1 = x.data.ctypes.data
+ id1 = x.data.ctypes._data
x += 1.
- assert_(id1 == x.data.ctypes.data)
+ assert_(id1 == x.data.ctypes._data)
assert_equal(x, y + 1.)
def test_inplace_addition_array(self):