diff options
author | Eric Wieser <wieser.eric@gmail.com> | 2018-12-16 16:25:54 -0800 |
---|---|---|
committer | Eric Wieser <wieser.eric@gmail.com> | 2018-12-16 20:26:53 -0800 |
commit | e03283dda358db50e21c4f230c0d2d9d9174a002 (patch) | |
tree | c45be72d0cad3dbc5154341408a588ad0ecd1d70 /numpy | |
parent | 129fe22dafd519229e326fca6849151f9c686807 (diff) | |
download | numpy-e03283dda358db50e21c4f230c0d2d9d9174a002.tar.gz |
BUG: Ensure that arr.ctypes works on arrays with overlapping fields
These would previously fail in `from_buffer`, since these arrays cannot be used with the buffer protocol.
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/_internal.py | 39 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 9 |
2 files changed, 40 insertions, 8 deletions
diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py index 4b5fda65c..a5742983c 100644 --- a/numpy/core/_internal.py +++ b/numpy/core/_internal.py @@ -244,21 +244,44 @@ class _missing_ctypes(object): self.value = ptr +class _unsafe_first_element_pointer(object): + """ + Helper to allow viewing an array as a ctypes pointer to the first element + + This avoids: + * dealing with strides + * `.view` rejecting object-containing arrays + * `memoryview` not supporting overlapping fields + """ + def __init__(self, arr): + self.base = arr + + @property + def __array_interface__(self): + i = dict( + shape=(), + typestr='|V0', + data=(self.base.__array_interface__['data'][0], False), + strides=(), + version=3, + ) + return i + 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) + # convert to a 0d array that has a data pointer referrign to the start + # of arr. This holds a reference to arr. + simple_arr = np.asarray(_unsafe_first_element_pointer(arr)) + + # create a `char[0]` using the same memory. + c_arr = (ctypes.c_char * 0).from_buffer(simple_arr) + # finally cast to void* - return ctypes.cast(ctypes.pointer(arr), ctypes.c_void_p) + return ctypes.cast(ctypes.pointer(c_arr), ctypes.c_void_p) class _ctypes(object): diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 171b2ad7f..fa21018ec 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -7480,6 +7480,14 @@ class TestCTypes(object): np.array([1, 2, 3]), np.array([['one', 'two'], ['three', 'four']]), np.array((1, 2), dtype='i4,i4'), + np.zeros((2,), dtype= + np.dtype(dict( + formats=['<i4', '<i4'], + names=['a', 'b'], + offsets=[0, 2], + itemsize=6 + )) + ), np.array([None], dtype=object), np.array([]), np.empty((0, 0)), @@ -7488,6 +7496,7 @@ class TestCTypes(object): '1d', '2d', 'structured', + 'overlapping', 'object', 'empty', 'empty-2d', |