diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/_internal.py | 66 | ||||
-rw-r--r-- | numpy/core/src/multiarray/arraytypes.c.src | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/calculation.c | 33 | ||||
-rw-r--r-- | numpy/core/tests/test_numeric.py | 20 | ||||
-rw-r--r-- | numpy/core/tests/test_regression.py | 7 | ||||
-rw-r--r-- | numpy/ma/core.py | 41 | ||||
-rw-r--r-- | numpy/ma/tests/test_core.py | 15 | ||||
-rw-r--r-- | numpy/tests/test_ctypeslib.py | 20 |
8 files changed, 135 insertions, 69 deletions
diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py index 9990bacf0..1cf89aab0 100644 --- a/numpy/core/_internal.py +++ b/numpy/core/_internal.py @@ -444,46 +444,46 @@ _pep3118_standard_map = { } _pep3118_standard_typechars = ''.join(_pep3118_standard_map.keys()) -def _dtype_from_pep3118(spec): - class Stream(object): - def __init__(self, s): - self.s = s - self.byteorder = '@' +class _Stream(object): + def __init__(self, s): + self.s = s + self.byteorder = '@' - def advance(self, n): - res = self.s[:n] - self.s = self.s[n:] - return res + def advance(self, n): + res = self.s[:n] + self.s = self.s[n:] + return res - def consume(self, c): - if self.s[:len(c)] == c: - self.advance(len(c)) - return True - return False - - def consume_until(self, c): - if callable(c): - i = 0 - while i < len(self.s) and not c(self.s[i]): - i = i + 1 - return self.advance(i) - else: - i = self.s.index(c) - res = self.advance(i) - self.advance(len(c)) - return res + def consume(self, c): + if self.s[:len(c)] == c: + self.advance(len(c)) + return True + return False - @property - def next(self): - return self.s[0] + def consume_until(self, c): + if callable(c): + i = 0 + while i < len(self.s) and not c(self.s[i]): + i = i + 1 + return self.advance(i) + else: + i = self.s.index(c) + res = self.advance(i) + self.advance(len(c)) + return res - def __bool__(self): - return bool(self.s) - __nonzero__ = __bool__ + @property + def next(self): + return self.s[0] - stream = Stream(spec) + def __bool__(self): + return bool(self.s) + __nonzero__ = __bool__ + +def _dtype_from_pep3118(spec): + stream = _Stream(spec) dtype, align = __dtype_from_pep3118(stream, is_subdtype=False) return dtype diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index b4158ec8e..d622effe6 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -733,7 +733,7 @@ VOID_getitem(void *input, void *vap) return (PyObject *)ret; } - return PyBytes_FromStringAndSize(PyArray_DATA(ap), descr->elsize); + return PyBytes_FromStringAndSize(ip, descr->elsize); } diff --git a/numpy/core/src/multiarray/calculation.c b/numpy/core/src/multiarray/calculation.c index e47dd81b9..90ee2c5b2 100644 --- a/numpy/core/src/multiarray/calculation.c +++ b/numpy/core/src/multiarray/calculation.c @@ -5,6 +5,7 @@ #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE #include "numpy/arrayobject.h" +#include "lowlevel_strided_loops.h" #include "npy_config.h" @@ -1102,7 +1103,18 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o if (out == newin) { outgood = 1; } - if (!outgood && PyArray_ISONESEGMENT(out) && + + + /* make sure the shape of the output array is the same */ + if (!PyArray_SAMESHAPE(newin, out)) { + PyErr_SetString(PyExc_ValueError, "clip: Output array must have the" + "same shape as the input."); + goto fail; + } + + if (!outgood && PyArray_EQUIVALENTLY_ITERABLE( + self, out, PyArray_TRIVIALLY_ITERABLE_OP_READ, + PyArray_TRIVIALLY_ITERABLE_OP_NOREAD) && PyArray_CHKFLAGS(out, NPY_ARRAY_ALIGNED) && PyArray_ISNOTSWAPPED(out) && PyArray_EquivTypes(PyArray_DESCR(out), indescr)) { @@ -1111,15 +1123,19 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o /* * Do we still not have a suitable output array? - * Create one, now + * Create one, now. No matter why the array is not suitable a copy has + * to be made. This may be just to avoid memory overlap though. */ if (!outgood) { int oflags; - if (PyArray_ISFORTRAN(out)) + if (PyArray_ISFORTRAN(self)) { oflags = NPY_ARRAY_FARRAY; - else + } + else { oflags = NPY_ARRAY_CARRAY; - oflags |= NPY_ARRAY_WRITEBACKIFCOPY | NPY_ARRAY_FORCECAST; + } + oflags |= (NPY_ARRAY_WRITEBACKIFCOPY | NPY_ARRAY_FORCECAST | + NPY_ARRAY_ENSURECOPY); Py_INCREF(indescr); newout = (PyArrayObject*)PyArray_FromArray(out, indescr, oflags); if (newout == NULL) { @@ -1131,13 +1147,6 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o Py_INCREF(newout); } - /* make sure the shape of the output array is the same */ - if (!PyArray_SAMESHAPE(newin, newout)) { - PyErr_SetString(PyExc_ValueError, "clip: Output array must have the" - "same shape as the input."); - goto fail; - } - /* Now we can call the fast-clip function */ min_data = max_data = NULL; if (mina != NULL) { diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 53486dc51..a1a92ef32 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -1530,7 +1530,7 @@ class TestClip(object): m = -0.5 M = 0.6 self.fastclip(a, m, M, a) - self.clip(a, m, M, ac) + self.clip(ac, m, M, ac) assert_array_strict_equal(a, ac) def test_noncontig_inplace(self): @@ -1543,7 +1543,7 @@ class TestClip(object): m = -0.5 M = 0.6 self.fastclip(a, m, M, a) - self.clip(a, m, M, ac) + self.clip(ac, m, M, ac) assert_array_equal(a, ac) def test_type_cast_01(self): @@ -1722,6 +1722,22 @@ class TestClip(object): self.clip(a, m, M, act) assert_array_strict_equal(ac, act) + def test_clip_with_out_transposed(self): + # Test that the out argument works when tranposed + a = np.arange(16).reshape(4, 4) + out = np.empty_like(a).T + a.clip(4, 10, out=out) + expected = self.clip(a, 4, 10) + assert_array_equal(out, expected) + + def test_clip_with_out_memory_overlap(self): + # Test that the out argument works when it has memory overlap + a = np.arange(16).reshape(4, 4) + ac = a.copy() + a[:-1].clip(4, 10, out=a[1:]) + expected = self.clip(ac[:-1], 4, 10) + assert_array_equal(a[1:], expected) + def test_clip_inplace_array(self): # Test native double input with array min/max a = self._generate_data(self.nr, self.nc) diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py index 5f4410d54..36478ddb7 100644 --- a/numpy/core/tests/test_regression.py +++ b/numpy/core/tests/test_regression.py @@ -2368,6 +2368,13 @@ class TestRegression(object): del va assert_equal(x, b'\x00\x00\x00\x00') + def test_void_getitem(self): + # Test fix for gh-11668. + assert_(np.array([b'a'], 'V1').astype('O') == b'a') + assert_(np.array([b'ab'], 'V2').astype('O') == b'ab') + assert_(np.array([b'abc'], 'V3').astype('O') == b'abc') + assert_(np.array([b'abcd'], 'V4').astype('O') == b'abcd') + def test_structarray_title(self): # The following used to segfault on pypy, due to NPY_TITLE_KEY # not working properly and resulting to double-decref of the diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 74edeb274..65ce967ae 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -215,7 +215,7 @@ def _recursive_fill_value(dtype, f): """ Recursively produce a fill value for `dtype`, calling f on scalar dtypes """ - if dtype.names: + if dtype.names is not None: vals = tuple(_recursive_fill_value(dtype[name], f) for name in dtype.names) return np.array(vals, dtype=dtype)[()] # decay to void scalar from 0d elif dtype.subdtype: @@ -433,7 +433,7 @@ def _recursive_set_fill_value(fillvalue, dt): if cdtype.subdtype: cdtype = cdtype.subdtype[0] - if cdtype.names: + if cdtype.names is not None: output_value.append(tuple(_recursive_set_fill_value(fval, cdtype))) else: output_value.append(np.array(fval, dtype=cdtype).item()) @@ -1282,7 +1282,7 @@ def _replace_dtype_fields_recursive(dtype, primitive_dtype): _recurse = _replace_dtype_fields_recursive # Do we have some name fields ? - if dtype.names: + if dtype.names is not None: descr = [] for name in dtype.names: field = dtype.fields[name] @@ -1547,7 +1547,7 @@ def _shrink_mask(m): """ Shrink a mask to nomask if possible """ - if not m.dtype.names and not m.any(): + if m.dtype.names is None and not m.any(): return nomask else: return m @@ -1733,7 +1733,7 @@ def mask_or(m1, m2, copy=False, shrink=True): names = m1.dtype.names for name in names: current1 = m1[name] - if current1.dtype.names: + if current1.dtype.names is not None: _recursive_mask_or(current1, m2[name], newmask[name]) else: umath.logical_or(current1, m2[name], newmask[name]) @@ -1750,7 +1750,7 @@ def mask_or(m1, m2, copy=False, shrink=True): (dtype1, dtype2) = (getattr(m1, 'dtype', None), getattr(m2, 'dtype', None)) if (dtype1 != dtype2): raise ValueError("Incompatible dtypes '%s'<>'%s'" % (dtype1, dtype2)) - if dtype1.names: + if dtype1.names is not None: # Allocate an output mask array with the properly broadcast shape. newmask = np.empty(np.broadcast(m1, m2).shape, dtype1) _recursive_mask_or(m1, m2, newmask) @@ -1793,7 +1793,7 @@ def flatten_mask(mask): def _flatmask(mask): "Flatten the mask and returns a (maybe nested) sequence of booleans." mnames = mask.dtype.names - if mnames: + if mnames is not None: return [flatten_mask(mask[name]) for name in mnames] else: return mask @@ -2431,7 +2431,7 @@ def _recursive_printoption(result, mask, printopt): """ names = result.dtype.names - if names: + if names is not None: for name in names: curdata = result[name] curmask = mask[name] @@ -2483,7 +2483,7 @@ def _recursive_filled(a, mask, fill_value): names = a.dtype.names for name in names: current = a[name] - if current.dtype.names: + if current.dtype.names is not None: _recursive_filled(current, mask[name], fill_value[name]) else: np.copyto(current, fill_value[name], where=mask[name]) @@ -2870,12 +2870,12 @@ class MaskedArray(ndarray): _data._mask = mask _data._sharedmask = not copy else: - if _data.dtype.names: + if _data.dtype.names is not None: def _recursive_or(a, b): "do a|=b on each field of a, recursively" for name in a.dtype.names: (af, bf) = (a[name], b[name]) - if af.dtype.names: + if af.dtype.names is not None: _recursive_or(af, bf) else: af |= bf @@ -2962,7 +2962,7 @@ class MaskedArray(ndarray): if isinstance(obj, ndarray): # XX: This looks like a bug -- shouldn't it check self.dtype # instead? - if obj.dtype.names: + if obj.dtype.names is not None: _mask = getmaskarray(obj) else: _mask = getmask(obj) @@ -3011,7 +3011,7 @@ class MaskedArray(ndarray): # When _mask.shape is not writable (because it's a void) pass # Finalize the fill_value for structured arrays - if self.dtype.names: + if self.dtype.names is not None: if self._fill_value is None: self._fill_value = _check_fill_value(None, self.dtype) return @@ -3295,15 +3295,14 @@ class MaskedArray(ndarray): return _dtype = _data.dtype - nbfields = len(_dtype.names or ()) if value is masked: # The mask wasn't set: create a full version. if _mask is nomask: _mask = self._mask = make_mask_none(self.shape, _dtype) # Now, set the mask to its value. - if nbfields: - _mask[indx] = tuple([True] * nbfields) + if _dtype.names is not None: + _mask[indx] = tuple([True] * len(_dtype.names)) else: _mask[indx] = True return @@ -3312,8 +3311,8 @@ class MaskedArray(ndarray): dval = getattr(value, '_data', value) # Get the _mask part of the new value mval = getmask(value) - if nbfields and mval is nomask: - mval = tuple([False] * nbfields) + if _dtype.names is not None and mval is nomask: + mval = tuple([False] * len(_dtype.names)) if _mask is nomask: # Set the data, then the mask _data[indx] = dval @@ -3328,7 +3327,7 @@ class MaskedArray(ndarray): indx = indx * umath.logical_not(_mask) _data[indx] = dval else: - if nbfields: + if _dtype.names is not None: err_msg = "Flexible 'hard' masks are not yet supported." raise NotImplementedError(err_msg) mindx = mask_or(_mask[indx], mval, copy=True) @@ -3709,7 +3708,7 @@ class MaskedArray(ndarray): if self is masked_singleton: return np.asanyarray(fill_value) - if m.dtype.names: + if m.dtype.names is not None: result = self._data.copy('K') _recursive_filled(result, self._mask, fill_value) elif not m.any(): @@ -3979,7 +3978,7 @@ class MaskedArray(ndarray): mask = mask_or(smask, omask, copy=True) odata = getdata(other) - if mask.dtype.names: + if mask.dtype.names is not None: # For possibly masked structured arrays we need to be careful, # since the standard structured array comparison will use all # fields, masked or not. To avoid masked fields influencing the diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index 67a9186a8..129809b5d 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -5022,3 +5022,18 @@ def test_astype(): x_f2 = np.array(x, dtype=x.dtype, order='F', subok=True) assert_(x_f2.flags.f_contiguous) assert_(x_f2.mask.flags.f_contiguous) + + +def test_fieldless_void(): + dt = np.dtype([]) # a void dtype with no fields + x = np.empty(4, dt) + + # these arrays contain no values, so there's little to test - but this + # shouldn't crash + mx = np.ma.array(x) + assert_equal(mx.dtype, x.dtype) + assert_equal(mx.shape, x.shape) + + mx = np.ma.array(x, mask=x) + assert_equal(mx.dtype, x.dtype) + assert_equal(mx.shape, x.shape) diff --git a/numpy/tests/test_ctypeslib.py b/numpy/tests/test_ctypeslib.py index 75ce9c8ca..62793a9d6 100644 --- a/numpy/tests/test_ctypeslib.py +++ b/numpy/tests/test_ctypeslib.py @@ -170,3 +170,23 @@ class TestAsArray(object): check(as_array(pointer(c_array), shape=())) check(as_array(pointer(c_array[0]), shape=(2,))) check(as_array(pointer(c_array[0][0]), shape=(2, 3))) + + def test_reference_cycles(self): + # related to gh-6511 + import ctypes + + # create array to work with + # don't use int/long to avoid running into bpo-10746 + N = 100 + a = np.arange(N, dtype=np.short) + + # get pointer to array + pnt = np.ctypeslib.as_ctypes(a) + + with np.testing.assert_no_gc_cycles(): + # decay the array above to a pointer to its first element + newpnt = ctypes.cast(pnt, ctypes.POINTER(ctypes.c_short)) + # and construct an array using this data + b = np.ctypeslib.as_array(newpnt, (N,)) + # now delete both, which should cleanup both objects + del newpnt, b |