diff options
author | Eric Wieser <wieser.eric@gmail.com> | 2019-09-13 00:49:54 -0700 |
---|---|---|
committer | Eric Wieser <wieser.eric@gmail.com> | 2019-09-13 00:49:54 -0700 |
commit | e4878891c848d0b1a46a310fd4a88fd7801b4fab (patch) | |
tree | 347007adf0c9dd4e4f4b4414106383a5c867b6ec /numpy/core/tests | |
parent | b12a8690b6383e03573237b65fddd859afa1f282 (diff) | |
parent | 27d77ce2219d9e573a57159ce997e495b8aecbc5 (diff) | |
download | numpy-e4878891c848d0b1a46a310fd4a88fd7801b4fab.tar.gz |
Merge tag 'branch-points/1.17.x' into HEAD
Diffstat (limited to 'numpy/core/tests')
28 files changed, 2144 insertions, 374 deletions
diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py index 9755e7b36..32e2ea537 100644 --- a/numpy/core/tests/test_api.py +++ b/numpy/core/tests/test_api.py @@ -3,8 +3,10 @@ from __future__ import division, absolute_import, print_function import sys import numpy as np +import pytest from numpy.testing import ( - assert_, assert_equal, assert_array_equal, assert_raises, HAS_REFCOUNT + assert_, assert_equal, assert_array_equal, assert_raises, assert_warns, + HAS_REFCOUNT ) # Switch between new behaviour when NPY_RELAXED_STRIDES_CHECKING is set. @@ -289,6 +291,14 @@ def test_array_astype(): a = np.array(1000, dtype='i4') assert_raises(TypeError, a.astype, 'U1', casting='safe') +@pytest.mark.parametrize("t", + np.sctypes['uint'] + np.sctypes['int'] + np.sctypes['float'] +) +def test_array_astype_warning(t): + # test ComplexWarning when casting from complex to float or int + a = np.array(10, dtype=np.complex) + assert_warns(np.ComplexWarning, a.astype, t) + def test_copyto_fromscalar(): a = np.arange(6, dtype='f4').reshape(2, 3) diff --git a/numpy/core/tests/test_arrayprint.py b/numpy/core/tests/test_arrayprint.py index 7a858d2e2..f2b8fdca7 100644 --- a/numpy/core/tests/test_arrayprint.py +++ b/numpy/core/tests/test_arrayprint.py @@ -90,6 +90,7 @@ class TestArrayRepr(object): assert_equal(repr(x), 'sub(sub(sub(..., dtype=object), dtype=object), dtype=object)') assert_equal(str(x), '...') + x[()] = 0 # resolve circular references for garbage collector # nested 0d-subclass-object x = sub(None) @@ -124,11 +125,13 @@ class TestArrayRepr(object): arr0d[()] = arr0d assert_equal(repr(arr0d), 'array(array(..., dtype=object), dtype=object)') + arr0d[()] = 0 # resolve recursion for garbage collector arr1d = np.array([None, None]) arr1d[1] = arr1d assert_equal(repr(arr1d), 'array([None, array(..., dtype=object)], dtype=object)') + arr1d[1] = 0 # resolve recursion for garbage collector first = np.array(None) second = np.array(None) @@ -136,6 +139,7 @@ class TestArrayRepr(object): second[()] = first assert_equal(repr(first), 'array(array(array(..., dtype=object), dtype=object), dtype=object)') + first[()] = 0 # resolve circular references for garbage collector def test_containing_list(self): # printing square brackets directly would be ambiguuous diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index b2ce0402a..f99c0f72b 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -9,7 +9,7 @@ from numpy.testing import ( assert_, assert_equal, assert_raises, assert_warns, suppress_warnings, assert_raises_regex, ) -from numpy.core.numeric import pickle +from numpy.compat import pickle # Use pytz to test out various time zones if available try: @@ -1081,6 +1081,133 @@ class TestDateTime(object): check(np.timedelta64(0), f, nat) check(nat, f, nat) + @pytest.mark.parametrize("op1, op2, exp", [ + # m8 same units round down + (np.timedelta64(7, 's'), + np.timedelta64(4, 's'), + 1), + # m8 same units round down with negative + (np.timedelta64(7, 's'), + np.timedelta64(-4, 's'), + -2), + # m8 same units negative no round down + (np.timedelta64(8, 's'), + np.timedelta64(-4, 's'), + -2), + # m8 different units + (np.timedelta64(1, 'm'), + np.timedelta64(31, 's'), + 1), + # m8 generic units + (np.timedelta64(1890), + np.timedelta64(31), + 60), + # Y // M works + (np.timedelta64(2, 'Y'), + np.timedelta64('13', 'M'), + 1), + # handle 1D arrays + (np.array([1, 2, 3], dtype='m8'), + np.array([2], dtype='m8'), + np.array([0, 1, 1], dtype=np.int64)), + ]) + def test_timedelta_floor_divide(self, op1, op2, exp): + assert_equal(op1 // op2, exp) + + @pytest.mark.parametrize("op1, op2", [ + # div by 0 + (np.timedelta64(10, 'us'), + np.timedelta64(0, 'us')), + # div with NaT + (np.timedelta64('NaT'), + np.timedelta64(50, 'us')), + # special case for int64 min + # in integer floor division + (np.timedelta64(np.iinfo(np.int64).min), + np.timedelta64(-1)), + ]) + def test_timedelta_floor_div_warnings(self, op1, op2): + with assert_warns(RuntimeWarning): + actual = op1 // op2 + assert_equal(actual, 0) + assert_equal(actual.dtype, np.int64) + + @pytest.mark.parametrize("val1, val2", [ + # the smallest integer that can't be represented + # exactly in a double should be preserved if we avoid + # casting to double in floordiv operation + (9007199254740993, 1), + # stress the alternate floordiv code path where + # operand signs don't match and remainder isn't 0 + (9007199254740999, -2), + ]) + def test_timedelta_floor_div_precision(self, val1, val2): + op1 = np.timedelta64(val1) + op2 = np.timedelta64(val2) + actual = op1 // op2 + # Python reference integer floor + expected = val1 // val2 + assert_equal(actual, expected) + + @pytest.mark.parametrize("val1, val2", [ + # years and months sometimes can't be unambiguously + # divided for floor division operation + (np.timedelta64(7, 'Y'), + np.timedelta64(3, 's')), + (np.timedelta64(7, 'M'), + np.timedelta64(1, 'D')), + ]) + def test_timedelta_floor_div_error(self, val1, val2): + with assert_raises_regex(TypeError, "common metadata divisor"): + val1 // val2 + + @pytest.mark.parametrize("op1, op2", [ + # reuse the test cases from floordiv + (np.timedelta64(7, 's'), + np.timedelta64(4, 's')), + # m8 same units round down with negative + (np.timedelta64(7, 's'), + np.timedelta64(-4, 's')), + # m8 same units negative no round down + (np.timedelta64(8, 's'), + np.timedelta64(-4, 's')), + # m8 different units + (np.timedelta64(1, 'm'), + np.timedelta64(31, 's')), + # m8 generic units + (np.timedelta64(1890), + np.timedelta64(31)), + # Y // M works + (np.timedelta64(2, 'Y'), + np.timedelta64('13', 'M')), + # handle 1D arrays + (np.array([1, 2, 3], dtype='m8'), + np.array([2], dtype='m8')), + ]) + def test_timedelta_divmod(self, op1, op2): + expected = (op1 // op2, op1 % op2) + assert_equal(divmod(op1, op2), expected) + + @pytest.mark.parametrize("op1, op2", [ + # reuse cases from floordiv + # div by 0 + (np.timedelta64(10, 'us'), + np.timedelta64(0, 'us')), + # div with NaT + (np.timedelta64('NaT'), + np.timedelta64(50, 'us')), + # special case for int64 min + # in integer floor division + (np.timedelta64(np.iinfo(np.int64).min), + np.timedelta64(-1)), + ]) + def test_timedelta_divmod_warnings(self, op1, op2): + with assert_warns(RuntimeWarning): + expected = (op1 // op2, op1 % op2) + with assert_warns(RuntimeWarning): + actual = divmod(op1, op2) + assert_equal(actual, expected) + def test_datetime_divide(self): for dta, tda, tdb, tdc, tdd in \ [ @@ -1111,8 +1238,6 @@ class TestDateTime(object): assert_equal(tda / tdd, 60.0) assert_equal(tdd / tda, 1.0 / 60.0) - # m8 // m8 - assert_raises(TypeError, np.floor_divide, tda, tdb) # int / m8 assert_raises(TypeError, np.divide, 2, tdb) # float / m8 @@ -1418,6 +1543,12 @@ class TestDateTime(object): assert_equal(x[0].astype(np.int64), 322689600000000000) + # gh-13062 + with pytest.raises(OverflowError): + np.datetime64(2**64, 'D') + with pytest.raises(OverflowError): + np.timedelta64(2**64, 'D') + def test_datetime_as_string(self): # Check all the units with default string conversion date = '1959-10-13' @@ -1680,7 +1811,7 @@ class TestDateTime(object): def test_timedelta_modulus_div_by_zero(self): with assert_warns(RuntimeWarning): actual = np.timedelta64(10, 's') % np.timedelta64(0, 's') - assert_equal(actual, np.timedelta64(0, 's')) + assert_equal(actual, np.timedelta64('NaT')) @pytest.mark.parametrize("val1, val2", [ # cases where one operand is not @@ -1694,7 +1825,7 @@ class TestDateTime(object): # NOTE: some of the operations may be supported # in the future with assert_raises_regex(TypeError, - "remainder cannot use operands with types"): + "'remainder' cannot use operands with types"): val1 % val2 def test_timedelta_arange_no_dtype(self): @@ -2077,6 +2208,27 @@ class TestDateTime(object): continue assert_raises(TypeError, np.isnat, np.zeros(10, t)) + def test_isfinite(self): + assert_(not np.isfinite(np.datetime64('NaT', 'ms'))) + assert_(not np.isfinite(np.datetime64('NaT', 'ns'))) + assert_(np.isfinite(np.datetime64('2038-01-19T03:14:07'))) + + assert_(not np.isfinite(np.timedelta64('NaT', "ms"))) + assert_(np.isfinite(np.timedelta64(34, "ms"))) + + res = np.array([True, True, False]) + for unit in ['Y', 'M', 'W', 'D', + 'h', 'm', 's', 'ms', 'us', + 'ns', 'ps', 'fs', 'as']: + arr = np.array([123, -321, "NaT"], dtype='<datetime64[%s]' % unit) + assert_equal(np.isfinite(arr), res) + arr = np.array([123, -321, "NaT"], dtype='>datetime64[%s]' % unit) + assert_equal(np.isfinite(arr), res) + arr = np.array([123, -321, "NaT"], dtype='<timedelta64[%s]' % unit) + assert_equal(np.isfinite(arr), res) + arr = np.array([123, -321, "NaT"], dtype='>timedelta64[%s]' % unit) + assert_equal(np.isfinite(arr), res) + def test_corecursive_input(self): # construct a co-recursive list a, b = [], [] @@ -2089,6 +2241,44 @@ class TestDateTime(object): assert_raises(RecursionError, obj_arr.astype, 'M8') assert_raises(RecursionError, obj_arr.astype, 'm8') + @pytest.mark.parametrize("time_unit", [ + "Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns", "ps", "fs", "as", + # compound units + "10D", "2M", + ]) + def test_limit_symmetry(self, time_unit): + """ + Dates should have symmetric limits around the unix epoch at +/-np.int64 + """ + epoch = np.datetime64(0, time_unit) + latest = np.datetime64(np.iinfo(np.int64).max, time_unit) + earliest = np.datetime64(-np.iinfo(np.int64).max, time_unit) + + # above should not have overflowed + assert earliest < epoch < latest + + @pytest.mark.parametrize("time_unit", [ + "Y", "M", + pytest.param("W", marks=pytest.mark.xfail(reason="gh-13197")), + "D", "h", "m", + "s", "ms", "us", "ns", "ps", "fs", "as", + pytest.param("10D", marks=pytest.mark.xfail(reason="similar to gh-13197")), + ]) + @pytest.mark.parametrize("sign", [-1, 1]) + def test_limit_str_roundtrip(self, time_unit, sign): + """ + Limits should roundtrip when converted to strings. + + This tests the conversion to and from npy_datetimestruct. + """ + # TODO: add absolute (gold standard) time span limit strings + limit = np.datetime64(np.iinfo(np.int64).max * sign, time_unit) + + # Convert to string and back. Explicit unit needed since the day and + # week reprs are not distinguishable. + limit_via_str = np.datetime64(str(limit), time_unit) + assert limit_via_str == limit + class TestDateTimeData(object): diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index edb5d5e46..6d71fcbd6 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -533,3 +533,18 @@ class Test_GetSet_NumericOps(_DeprecationTestCase): # other tests. self.assert_deprecated(np.set_numeric_ops, kwargs={}) assert_raises(ValueError, np.set_numeric_ops, add='abc') + + +class TestShape1Fields(_DeprecationTestCase): + warning_cls = FutureWarning + + # 2019-05-20, 1.17.0 + def test_shape_1_fields(self): + self.assert_deprecated(np.dtype, args=([('a', int, 1)],)) + + +class TestNonZero(_DeprecationTestCase): + # 2019-05-26, 1.17.0 + def test_zerod(self): + self.assert_deprecated(lambda: np.nonzero(np.array(0))) + self.assert_deprecated(lambda: np.nonzero(np.array(1))) diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index c55751e3c..d24ab98e3 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -4,11 +4,14 @@ import sys import operator import pytest import ctypes +import gc import numpy as np from numpy.core._rational_tests import rational -from numpy.testing import assert_, assert_equal, assert_raises -from numpy.core.numeric import pickle +from numpy.testing import ( + assert_, assert_equal, assert_array_equal, assert_raises, HAS_REFCOUNT) +from numpy.compat import pickle +from itertools import permutations def assert_dtype_equal(a, b): assert_equal(a, b) @@ -136,6 +139,18 @@ class TestRecord(object): 'titles': ['RRed pixel', 'Blue pixel']}) assert_dtype_not_equal(a, b) + @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") + def test_refcount_dictionary_setting(self): + names = ["name1"] + formats = ["f8"] + titles = ["t1"] + offsets = [0] + d = dict(names=names, formats=formats, titles=titles, offsets=offsets) + refcounts = {k: sys.getrefcount(i) for k, i in d.items()} + np.dtype(d) + refcounts_new = {k: sys.getrefcount(i) for k, i in d.items()} + assert refcounts == refcounts_new + def test_mutate(self): # Mutating a dtype should reset the cached hash value a = np.dtype([('yo', int)]) @@ -213,7 +228,6 @@ class TestRecord(object): assert_equal(dt1.descr, [('a', '|i1'), ('', '|V3'), ('b', [('f0', '<i2'), ('', '|V2'), ('f1', '<f4')], (2,))]) - def test_union_struct(self): # Should be able to create union dtypes @@ -315,10 +329,66 @@ class TestRecord(object): assert_raises(IndexError, lambda: dt[-3]) assert_raises(TypeError, operator.getitem, dt, 3.0) - assert_raises(TypeError, operator.getitem, dt, []) assert_equal(dt[1], dt[np.int8(1)]) + @pytest.mark.parametrize('align_flag',[False, True]) + def test_multifield_index(self, align_flag): + # indexing with a list produces subfields + # the align flag should be preserved + dt = np.dtype([ + (('title', 'col1'), '<U20'), ('A', '<f8'), ('B', '<f8') + ], align=align_flag) + + dt_sub = dt[['B', 'col1']] + assert_equal( + dt_sub, + np.dtype({ + 'names': ['B', 'col1'], + 'formats': ['<f8', '<U20'], + 'offsets': [88, 0], + 'titles': [None, 'title'], + 'itemsize': 96 + }) + ) + assert_equal(dt_sub.isalignedstruct, align_flag) + + dt_sub = dt[['B']] + assert_equal( + dt_sub, + np.dtype({ + 'names': ['B'], + 'formats': ['<f8'], + 'offsets': [88], + 'itemsize': 96 + }) + ) + assert_equal(dt_sub.isalignedstruct, align_flag) + + dt_sub = dt[[]] + assert_equal( + dt_sub, + np.dtype({ + 'names': [], + 'formats': [], + 'offsets': [], + 'itemsize': 96 + }) + ) + assert_equal(dt_sub.isalignedstruct, align_flag) + + assert_raises(TypeError, operator.getitem, dt, ()) + assert_raises(TypeError, operator.getitem, dt, [1, 2, 3]) + assert_raises(TypeError, operator.getitem, dt, ['col1', 2]) + assert_raises(KeyError, operator.getitem, dt, ['fake']) + assert_raises(KeyError, operator.getitem, dt, ['title']) + assert_raises(ValueError, operator.getitem, dt, ['col1', 'col1']) + + def test_partial_dict(self): + # 'names' is missing + assert_raises(ValueError, np.dtype, + {'formats': ['i4', 'i4'], 'f0': ('i4', 0), 'f1':('i4', 4)}) + class TestSubarray(object): def test_single_subarray(self): @@ -352,7 +422,10 @@ class TestSubarray(object): def test_shape_equal(self): """Test some data types that are equal""" assert_dtype_equal(np.dtype('f8'), np.dtype(('f8', tuple()))) - assert_dtype_equal(np.dtype('f8'), np.dtype(('f8', 1))) + # FutureWarning during deprecation period; after it is passed this + # should instead check that "(1)f8" == "1f8" == ("f8", 1). + with pytest.warns(FutureWarning): + assert_dtype_equal(np.dtype('f8'), np.dtype(('f8', 1))) assert_dtype_equal(np.dtype((int, 2)), np.dtype((int, (2,)))) assert_dtype_equal(np.dtype(('<f4', (3, 2))), np.dtype(('<f4', (3, 2)))) d = ([('a', 'f4', (1, 2)), ('b', 'f8', (3, 1))], (3, 2)) @@ -441,11 +514,178 @@ class TestSubarray(object): def test_alignment(self): #Check that subarrays are aligned - t1 = np.dtype('1i4', align=True) + t1 = np.dtype('(1,)i4', align=True) t2 = np.dtype('2i4', align=True) assert_equal(t1.alignment, t2.alignment) +def iter_struct_object_dtypes(): + """ + Iterates over a few complex dtypes and object pattern which + fill the array with a given object (defaults to a singleton). + + Yields + ------ + dtype : dtype + pattern : tuple + Structured tuple for use with `np.array`. + count : int + Number of objects stored in the dtype. + singleton : object + A singleton object. The returned pattern is constructed so that + all objects inside the datatype are set to the singleton. + """ + obj = object() + + dt = np.dtype([('b', 'O', (2, 3))]) + p = ([[obj] * 3] * 2,) + yield pytest.param(dt, p, 6, obj, id="<subarray>") + + dt = np.dtype([('a', 'i4'), ('b', 'O', (2, 3))]) + p = (0, [[obj] * 3] * 2) + yield pytest.param(dt, p, 6, obj, id="<subarray in field>") + + dt = np.dtype([('a', 'i4'), + ('b', [('ba', 'O'), ('bb', 'i1')], (2, 3))]) + p = (0, [[(obj, 0)] * 3] * 2) + yield pytest.param(dt, p, 6, obj, id="<structured subarray 1>") + + dt = np.dtype([('a', 'i4'), + ('b', [('ba', 'O'), ('bb', 'O')], (2, 3))]) + p = (0, [[(obj, obj)] * 3] * 2) + yield pytest.param(dt, p, 12, obj, id="<structured subarray 2>") + + +@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") +class TestStructuredObjectRefcounting: + """These tests cover various uses of complicated structured types which + include objects and thus require reference counting. + """ + @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'], + iter_struct_object_dtypes()) + @pytest.mark.parametrize(["creation_func", "creation_obj"], [ + pytest.param(np.empty, None, + # None is probably used for too many things + marks=pytest.mark.skip("unreliable due to python's behaviour")), + (np.ones, 1), + (np.zeros, 0)]) + def test_structured_object_create_delete(self, dt, pat, count, singleton, + creation_func, creation_obj): + """Structured object reference counting in creation and deletion""" + # The test assumes that 0, 1, and None are singletons. + gc.collect() + before = sys.getrefcount(creation_obj) + arr = creation_func(3, dt) + + now = sys.getrefcount(creation_obj) + assert now - before == count * 3 + del arr + now = sys.getrefcount(creation_obj) + assert now == before + + @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'], + iter_struct_object_dtypes()) + def test_structured_object_item_setting(self, dt, pat, count, singleton): + """Structured object reference counting for simple item setting""" + one = 1 + + gc.collect() + before = sys.getrefcount(singleton) + arr = np.array([pat] * 3, dt) + assert sys.getrefcount(singleton) - before == count * 3 + # Fill with `1` and check that it was replaced correctly: + before2 = sys.getrefcount(one) + arr[...] = one + after2 = sys.getrefcount(one) + assert after2 - before2 == count * 3 + del arr + gc.collect() + assert sys.getrefcount(one) == before2 + assert sys.getrefcount(singleton) == before + + @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'], + iter_struct_object_dtypes()) + @pytest.mark.parametrize( + ['shape', 'index', 'items_changed'], + [((3,), ([0, 2],), 2), + ((3, 2), ([0, 2], slice(None)), 4), + ((3, 2), ([0, 2], [1]), 2), + ((3,), ([True, False, True]), 2)]) + def test_structured_object_indexing(self, shape, index, items_changed, + dt, pat, count, singleton): + """Structured object reference counting for advanced indexing.""" + zero = 0 + one = 1 + + arr = np.zeros(shape, dt) + + gc.collect() + before_zero = sys.getrefcount(zero) + before_one = sys.getrefcount(one) + # Test item getting: + part = arr[index] + after_zero = sys.getrefcount(zero) + assert after_zero - before_zero == count * items_changed + del part + # Test item setting: + arr[index] = one + gc.collect() + after_zero = sys.getrefcount(zero) + after_one = sys.getrefcount(one) + assert before_zero - after_zero == count * items_changed + assert after_one - before_one == count * items_changed + + @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'], + iter_struct_object_dtypes()) + def test_structured_object_take_and_repeat(self, dt, pat, count, singleton): + """Structured object reference counting for specialized functions. + The older functions such as take and repeat use different code paths + then item setting (when writing this). + """ + indices = [0, 1] + + arr = np.array([pat] * 3, dt) + gc.collect() + before = sys.getrefcount(singleton) + res = arr.take(indices) + after = sys.getrefcount(singleton) + assert after - before == count * 2 + new = res.repeat(10) + gc.collect() + after_repeat = sys.getrefcount(singleton) + assert after_repeat - after == count * 2 * 10 + + +class TestStructuredDtypeSparseFields(object): + """Tests subarray fields which contain sparse dtypes so that + not all memory is used by the dtype work. Such dtype's should + leave the underlying memory unchanged. + """ + dtype = np.dtype([('a', {'names':['aa', 'ab'], 'formats':['f', 'f'], + 'offsets':[0, 4]}, (2, 3))]) + sparse_dtype = np.dtype([('a', {'names':['ab'], 'formats':['f'], + 'offsets':[4]}, (2, 3))]) + + @pytest.mark.xfail(reason="inaccessible data is changed see gh-12686.") + @pytest.mark.valgrind_error(reason="reads from uninitialized buffers.") + def test_sparse_field_assignment(self): + arr = np.zeros(3, self.dtype) + sparse_arr = arr.view(self.sparse_dtype) + + sparse_arr[...] = np.finfo(np.float32).max + # dtype is reduced when accessing the field, so shape is (3, 2, 3): + assert_array_equal(arr["a"]["aa"], np.zeros((3, 2, 3))) + + def test_sparse_field_assignment_fancy(self): + # Fancy assignment goes to the copyswap function for comlex types: + arr = np.zeros(3, self.dtype) + sparse_arr = arr.view(self.sparse_dtype) + + sparse_arr[[0, 1, 2]] = np.finfo(np.float32).max + # dtype is reduced when accessing the field, so shape is (3, 2, 3): + assert_array_equal(arr["a"]["aa"], np.zeros((3, 2, 3))) + + class TestMonsterType(object): """Test deeply nested subtypes.""" @@ -951,3 +1191,18 @@ class TestFromCTypes(object): self.check(ctypes.c_uint16.__ctype_be__, np.dtype('>u2')) self.check(ctypes.c_uint8.__ctype_le__, np.dtype('u1')) self.check(ctypes.c_uint8.__ctype_be__, np.dtype('u1')) + + all_types = set(np.typecodes['All']) + all_pairs = permutations(all_types, 2) + + @pytest.mark.parametrize("pair", all_pairs) + def test_pairs(self, pair): + """ + Check that np.dtype('x,y') matches [np.dtype('x'), np.dtype('y')] + Example: np.dtype('d,I') -> dtype([('f0', '<f8'), ('f1', '<u4')]) + """ + # gh-5645: check that np.dtype('i,L') can be used + pair_type = np.dtype('{},{}'.format(*pair)) + expected = np.dtype([('f0', pair[0]), ('f1', pair[1])]) + assert_equal(pair_type, expected) + diff --git a/numpy/core/tests/test_einsum.py b/numpy/core/tests/test_einsum.py index 3be4a8a26..cfeeb8a90 100644 --- a/numpy/core/tests/test_einsum.py +++ b/numpy/core/tests/test_einsum.py @@ -5,7 +5,7 @@ import itertools import numpy as np from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_almost_equal, - assert_raises, suppress_warnings + assert_raises, suppress_warnings, assert_raises_regex ) # Setup for optimize einsum @@ -90,6 +90,11 @@ class TestEinsum(object): optimize=do_opt) assert_raises(ValueError, np.einsum, "i->i", [[0, 1], [0, 1]], out=np.arange(4).reshape(2, 2), optimize=do_opt) + with assert_raises_regex(ValueError, "'b'"): + # gh-11221 - 'c' erroneously appeared in the error message + a = np.ones((3, 3, 4, 5, 6)) + b = np.ones((3, 4, 5)) + np.einsum('aabcb,abc', a, b) def test_einsum_views(self): # pass-through diff --git a/numpy/core/tests/test_errstate.py b/numpy/core/tests/test_errstate.py index 670d485c1..0008c4cc8 100644 --- a/numpy/core/tests/test_errstate.py +++ b/numpy/core/tests/test_errstate.py @@ -39,3 +39,11 @@ class TestErrstate(object): with np.errstate(call=None): assert_(np.geterrcall() is None, 'call is not None') assert_(np.geterrcall() is olderrcall, 'call is not olderrcall') + + def test_errstate_decorator(self): + @np.errstate(all='ignore') + def foo(): + a = -np.arange(3) + a // 0 + + foo() diff --git a/numpy/core/tests/test_function_base.py b/numpy/core/tests/test_function_base.py index 459bacab0..8b820bd75 100644 --- a/numpy/core/tests/test_function_base.py +++ b/numpy/core/tests/test_function_base.py @@ -362,3 +362,9 @@ class TestLinspace(object): assert_(isinstance(y, tuple) and len(y) == 2 and len(y[0]) == num and isnan(y[1]), 'num={0}, endpoint={1}'.format(num, ept)) + + def test_object(self): + start = array(1, dtype='O') + stop = array(2, dtype='O') + y = linspace(start, stop, 3) + assert_array_equal(y, array([1., 1.5, 2.])) diff --git a/numpy/core/tests/test_half.py b/numpy/core/tests/test_half.py index b28c933db..1e1e6d7d9 100644 --- a/numpy/core/tests/test_half.py +++ b/numpy/core/tests/test_half.py @@ -69,6 +69,85 @@ class TestHalf(object): j = np.array(i_f16, dtype=int) assert_equal(i_int, j) + @pytest.mark.parametrize("offset", [None, "up", "down"]) + @pytest.mark.parametrize("shift", [None, "up", "down"]) + @pytest.mark.parametrize("float_t", [np.float32, np.float64]) + def test_half_conversion_rounding(self, float_t, shift, offset): + # Assumes that round to even is used during casting. + max_pattern = np.float16(np.finfo(np.float16).max).view(np.uint16) + + # Test all (positive) finite numbers, denormals are most interesting + # however: + f16s_patterns = np.arange(0, max_pattern+1, dtype=np.uint16) + f16s_float = f16s_patterns.view(np.float16).astype(float_t) + + # Shift the values by half a bit up or a down (or do not shift), + if shift == "up": + f16s_float = 0.5 * (f16s_float[:-1] + f16s_float[1:])[1:] + elif shift == "down": + f16s_float = 0.5 * (f16s_float[:-1] + f16s_float[1:])[:-1] + else: + f16s_float = f16s_float[1:-1] + + # Increase the float by a minimal value: + if offset == "up": + f16s_float = np.nextafter(f16s_float, float_t(1e50)) + elif offset == "down": + f16s_float = np.nextafter(f16s_float, float_t(-1e50)) + + # Convert back to float16 and its bit pattern: + res_patterns = f16s_float.astype(np.float16).view(np.uint16) + + # The above calculations tries the original values, or the exact + # mid points between the float16 values. It then further offsets them + # by as little as possible. If no offset occurs, "round to even" + # logic will be necessary, an arbitrarily small offset should cause + # normal up/down rounding always. + + # Calculate the expected pattern: + cmp_patterns = f16s_patterns[1:-1].copy() + + if shift == "down" and offset != "up": + shift_pattern = -1 + elif shift == "up" and offset != "down": + shift_pattern = 1 + else: + # There cannot be a shift, either shift is None, so all rounding + # will go back to original, or shift is reduced by offset too much. + shift_pattern = 0 + + # If rounding occurs, is it normal rounding or round to even? + if offset is None: + # Round to even occurs, modify only non-even, cast to allow + (-1) + cmp_patterns[0::2].view(np.int16)[...] += shift_pattern + else: + cmp_patterns.view(np.int16)[...] += shift_pattern + + assert_equal(res_patterns, cmp_patterns) + + @pytest.mark.parametrize(["float_t", "uint_t", "bits"], + [(np.float32, np.uint32, 23), + (np.float64, np.uint64, 52)]) + def test_half_conversion_denormal_round_even(self, float_t, uint_t, bits): + # Test specifically that all bits are considered when deciding + # whether round to even should occur (i.e. no bits are lost at the + # end. Compare also gh-12721. The most bits can get lost for the + # smallest denormal: + smallest_value = np.uint16(1).view(np.float16).astype(float_t) + assert smallest_value == 2**-24 + + # Will be rounded to zero based on round to even rule: + rounded_to_zero = smallest_value / float_t(2) + assert rounded_to_zero.astype(np.float16) == 0 + + # The significand will be all 0 for the float_t, test that we do not + # lose the lower ones of these: + for i in range(bits): + # slightly increasing the value should make it round up: + larger_pattern = rounded_to_zero.view(uint_t) | uint_t(1 << i) + larger_value = larger_pattern.view(float_t) + assert larger_value.astype(np.float16) == smallest_value + def test_nans_infs(self): with np.errstate(all='ignore'): # Check some of the ufuncs diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py index 99792cee7..f7485c3f7 100644 --- a/numpy/core/tests/test_indexing.py +++ b/numpy/core/tests/test_indexing.py @@ -249,6 +249,15 @@ class TestIndexing(object): [4, 0, 6], [0, 8, 0]]) + def test_boolean_indexing_list(self): + # Regression test for #13715. It's a use-after-free bug which the + # test won't directly catch, but it will show up in valgrind. + a = np.array([1, 2, 3]) + b = [True, False, True] + # Two variants of the test because the first takes a fast path + assert_equal(a[b], [1, 3]) + assert_equal(a[None, b], [[1, 3]]) + def test_reverse_strides_and_subspace_bufferinit(self): # This tests that the strides are not reversed for simple and # subspace fancy indexing. diff --git a/numpy/core/tests/test_item_selection.py b/numpy/core/tests/test_item_selection.py index 3bc24fc95..9bd246866 100644 --- a/numpy/core/tests/test_item_selection.py +++ b/numpy/core/tests/test_item_selection.py @@ -79,9 +79,9 @@ class TestTake(object): assert_array_equal(a, a_original) def test_empty_argpartition(self): - # In reference to github issue #6530 - a = np.array([0, 2, 4, 6, 8, 10]) - a = a.argpartition(np.array([], dtype=np.int16)) + # In reference to github issue #6530 + a = np.array([0, 2, 4, 6, 8, 10]) + a = a.argpartition(np.array([], dtype=np.int16)) - b = np.array([0, 1, 2, 3, 4, 5]) - assert_array_equal(a, b) + b = np.array([0, 1, 2, 3, 4, 5]) + assert_array_equal(a, b) diff --git a/numpy/core/tests/test_longdouble.py b/numpy/core/tests/test_longdouble.py index cf50d5d5c..ee4197f8f 100644 --- a/numpy/core/tests/test_longdouble.py +++ b/numpy/core/tests/test_longdouble.py @@ -1,5 +1,6 @@ from __future__ import division, absolute_import, print_function +import warnings import pytest import numpy as np @@ -205,3 +206,28 @@ class TestCommaDecimalPointLocale(CommaDecimalPointLocale): def test_fromstring_foreign_value(self): b = np.fromstring("1,234", dtype=np.longdouble, sep=" ") assert_array_equal(b[0], 1) + +@pytest.mark.parametrize("int_val", [ + # cases discussed in gh-10723 + # and gh-9968 + 2 ** 1024, 0]) +def test_longdouble_from_int(int_val): + # for issue gh-9968 + str_val = str(int_val) + # we'll expect a RuntimeWarning on platforms + # with np.longdouble equivalent to np.double + # for large integer input + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', '', RuntimeWarning) + # can be inf==inf on some platforms + assert np.longdouble(int_val) == np.longdouble(str_val) + # we can't directly compare the int and + # max longdouble value on all platforms + if np.allclose(np.finfo(np.longdouble).max, + np.finfo(np.double).max) and w: + assert w[0].category is RuntimeWarning + +@pytest.mark.parametrize("bool_val", [ + True, False]) +def test_longdouble_from_bool(bool_val): + assert np.longdouble(bool_val) == np.longdouble(int(bool_val)) diff --git a/numpy/core/tests/test_memmap.py b/numpy/core/tests/test_memmap.py index 990d0ae26..d2ae564b2 100644 --- a/numpy/core/tests/test_memmap.py +++ b/numpy/core/tests/test_memmap.py @@ -204,3 +204,13 @@ class TestMemmap(object): self.tmpfp.write(b'a'*16) mm = memmap(self.tmpfp, dtype='float64') assert_equal(mm.shape, (2,)) + + def test_empty_array(self): + # gh-12653 + with pytest.raises(ValueError, match='empty file'): + memmap(self.tmpfp, shape=(0,4), mode='w+') + + self.tmpfp.write(b'\0') + + # ok now the file is not empty + memmap(self.tmpfp, shape=(0,4), mode='w+') diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index cdacdabbe..1f21c5f4d 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -21,7 +21,15 @@ import weakref import pytest from contextlib import contextmanager -from numpy.core.numeric import pickle +from numpy.compat import pickle + +try: + import pathlib +except ImportError: + try: + import pathlib2 as pathlib + except ImportError: + pathlib = None if sys.version_info[0] >= 3: import builtins @@ -36,7 +44,7 @@ from numpy.testing import ( assert_, assert_raises, assert_warns, assert_equal, assert_almost_equal, assert_array_equal, assert_raises_regex, assert_array_almost_equal, assert_allclose, IS_PYPY, HAS_REFCOUNT, assert_array_less, runstring, - temppath, suppress_warnings + temppath, suppress_warnings, break_cycles, ) from numpy.core.tests._locales import CommaDecimalPointLocale @@ -54,7 +62,12 @@ else: def _aligned_zeros(shape, dtype=float, order="C", align=None): - """Allocate a new ndarray with aligned memory.""" + """ + Allocate a new ndarray with aligned memory. + + The ndarray is guaranteed *not* aligned to twice the requested alignment. + Eg, if align=4, guarantees it is not aligned to 8. If align=None uses + dtype.alignment.""" dtype = np.dtype(dtype) if dtype == np.dtype(object): # Can't do this, fall back to standard allocation (which @@ -67,10 +80,15 @@ def _aligned_zeros(shape, dtype=float, order="C", align=None): if not hasattr(shape, '__len__'): shape = (shape,) size = functools.reduce(operator.mul, shape) * dtype.itemsize - buf = np.empty(size + align + 1, np.uint8) - offset = buf.__array_interface__['data'][0] % align + buf = np.empty(size + 2*align + 1, np.uint8) + + ptr = buf.__array_interface__['data'][0] + offset = ptr % align if offset != 0: offset = align - offset + if (ptr % (2*align)) == 0: + offset += align + # Note: slices producing 0-size arrays do not necessarily change # data pointer --- so we use and allocate size+1 buf = buf[offset:offset+size+1][:-1] @@ -92,6 +110,39 @@ class TestFlags(object): self.a[0] = 5 self.a[0] = 0 + def test_writeable_any_base(self): + # Ensure that any base being writeable is sufficient to change flag; + # this is especially interesting for arrays from an array interface. + arr = np.arange(10) + + class subclass(np.ndarray): + pass + + # Create subclass so base will not be collapsed, this is OK to change + view1 = arr.view(subclass) + view2 = view1[...] + arr.flags.writeable = False + view2.flags.writeable = False + view2.flags.writeable = True # Can be set to True again. + + arr = np.arange(10) + + class frominterface: + def __init__(self, arr): + self.arr = arr + self.__array_interface__ = arr.__array_interface__ + + view1 = np.asarray(frominterface) + view2 = view1[...] + view2.flags.writeable = False + view2.flags.writeable = True + + view1.flags.writeable = False + view2.flags.writeable = False + with assert_raises(ValueError): + # Must assume not writeable, since only base is not: + view2.flags.writeable = True + def test_writeable_from_readonly(self): # gh-9440 - make sure fromstring, from buffer on readonly buffers # set writeable False @@ -121,6 +172,7 @@ class TestFlags(object): assert_(vals.flags.writeable) @pytest.mark.skipif(sys.version_info[0] < 3, reason="Python 2 always copies") + @pytest.mark.skipif(IS_PYPY, reason="PyPy always copies") def test_writeable_pickle(self): import pickle # Small arrays will be copied without setting base. @@ -132,6 +184,56 @@ class TestFlags(object): assert_(vals.flags.writeable) assert_(isinstance(vals.base, bytes)) + def test_writeable_from_c_data(self): + # Test that the writeable flag can be changed for an array wrapping + # low level C-data, but not owning its data. + # Also see that this is deprecated to change from python. + from numpy.core._multiarray_tests import get_c_wrapping_array + + arr_writeable = get_c_wrapping_array(True) + assert not arr_writeable.flags.owndata + assert arr_writeable.flags.writeable + view = arr_writeable[...] + + # Toggling the writeable flag works on the view: + view.flags.writeable = False + assert not view.flags.writeable + view.flags.writeable = True + assert view.flags.writeable + # Flag can be unset on the arr_writeable: + arr_writeable.flags.writeable = False + + arr_readonly = get_c_wrapping_array(False) + assert not arr_readonly.flags.owndata + assert not arr_readonly.flags.writeable + + for arr in [arr_writeable, arr_readonly]: + view = arr[...] + view.flags.writeable = False # make sure it is readonly + arr.flags.writeable = False + assert not arr.flags.writeable + + with assert_raises(ValueError): + view.flags.writeable = True + + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + with assert_raises(DeprecationWarning): + arr.flags.writeable = True + + with assert_warns(DeprecationWarning): + arr.flags.writeable = True + + def test_warnonwrite(self): + a = np.arange(10) + a.flags._warn_on_write = True + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always') + a[1] = 10 + a[2] = 10 + # only warn once + assert_(len(w) == 1) + def test_otherflags(self): assert_equal(self.a.flags.carray, True) assert_equal(self.a.flags['C'], True) @@ -867,6 +969,29 @@ class TestCreation(object): assert_equal(np.array([long(4), 2**80, long(4)]).dtype, object) assert_equal(np.array([2**80, long(4)]).dtype, object) + def test_sequence_of_array_like(self): + class ArrayLike: + def __init__(self): + self.__array_interface__ = { + "shape": (42,), + "typestr": "<i1", + "data": bytes(42) + } + + # Make sure __array_*__ is used instead of Sequence methods. + def __iter__(self): + raise AssertionError("__iter__ was called") + + def __getitem__(self, idx): + raise AssertionError("__getitem__ was called") + + def __len__(self): + return 42 + + assert_equal( + np.array([ArrayLike()]), + np.zeros((1, 42), dtype=np.byte)) + def test_non_sequence_sequence(self): """Should not segfault. @@ -1385,10 +1510,10 @@ class TestZeroSizeFlexible(object): sort_func(zs, kind=kind, **kwargs) def test_sort(self): - self._test_sort_partition('sort', kinds='qhm') + self._test_sort_partition('sort', kinds='qhs') def test_argsort(self): - self._test_sort_partition('argsort', kinds='qhm') + self._test_sort_partition('argsort', kinds='qhs') def test_partition(self): self._test_sort_partition('partition', kinds=['introselect'], kth=2) @@ -1413,6 +1538,10 @@ class TestZeroSizeFlexible(object): # viewing as any non-empty type gives an empty result assert_equal(zs.view((dt, 1)).shape, (0,)) + def test_dumps(self): + zs = self._zeros(10, int) + assert_equal(zs, pickle.loads(zs.dumps())) + def test_pickle(self): for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): for dt in [bytes, np.void, unicode]: @@ -1439,6 +1568,9 @@ class TestZeroSizeFlexible(object): class TestMethods(object): + + sort_kinds = ['quicksort', 'heapsort', 'stable'] + def test_compress(self): tgt = [[5, 6, 7, 8, 9]] arr = np.arange(10).reshape(2, 5) @@ -1478,6 +1610,11 @@ class TestMethods(object): # gh-12031, caused SEGFAULT assert_raises(TypeError, oned.choose,np.void(0), [oned]) + # gh-6272 check overlap on out + x = np.arange(5) + y = np.choose([0,0,0], [x[:3], x[:3], x[:3]], out=x[1:4], mode='wrap') + assert_equal(y, np.array([0, 1, 2])) + def test_prod(self): ba = [1, 2, 10, 11, 6, 5, 4] ba2 = [[1, 2, 3, 4], [5, 6, 7, 9], [10, 3, 4, 5]] @@ -1597,22 +1734,37 @@ class TestMethods(object): # of sorted items must be greater than ~50 to check the actual # algorithm because quick and merge sort fall over to insertion # sort for small arrays. - a = np.arange(101) - b = a[::-1].copy() - for kind in ['q', 'm', 'h']: - msg = "scalar sort, kind=%s" % kind - c = a.copy() - c.sort(kind=kind) - assert_equal(c, a, msg) - c = b.copy() - c.sort(kind=kind) - assert_equal(c, a, msg) + # Test unsigned dtypes and nonnegative numbers + for dtype in [np.uint8, np.uint16, np.uint32, np.uint64, np.float16, np.float32, np.float64, np.longdouble]: + a = np.arange(101, dtype=dtype) + b = a[::-1].copy() + for kind in self.sort_kinds: + msg = "scalar sort, kind=%s, dtype=%s" % (kind, dtype) + c = a.copy() + c.sort(kind=kind) + assert_equal(c, a, msg) + c = b.copy() + c.sort(kind=kind) + assert_equal(c, a, msg) + + # Test signed dtypes and negative numbers as well + for dtype in [np.int8, np.int16, np.int32, np.int64, np.float16, np.float32, np.float64, np.longdouble]: + a = np.arange(-50, 51, dtype=dtype) + b = a[::-1].copy() + for kind in self.sort_kinds: + msg = "scalar sort, kind=%s, dtype=%s" % (kind, dtype) + c = a.copy() + c.sort(kind=kind) + assert_equal(c, a, msg) + c = b.copy() + c.sort(kind=kind) + assert_equal(c, a, msg) # test complex sorts. These use the same code as the scalars # but the compare function differs. ai = a*1j + 1 bi = b*1j + 1 - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "complex sort, real part == 1, kind=%s" % kind c = ai.copy() c.sort(kind=kind) @@ -1622,7 +1774,7 @@ class TestMethods(object): assert_equal(c, ai, msg) ai = a + 1j bi = b + 1j - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "complex sort, imag part == 1, kind=%s" % kind c = ai.copy() c.sort(kind=kind) @@ -1644,7 +1796,7 @@ class TestMethods(object): s = 'aaaaaaaa' a = np.array([s + chr(i) for i in range(101)]) b = a[::-1].copy() - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "string sort, kind=%s" % kind c = a.copy() c.sort(kind=kind) @@ -1657,7 +1809,7 @@ class TestMethods(object): s = 'aaaaaaaa' a = np.array([s + chr(i) for i in range(101)], dtype=np.unicode) b = a[::-1].copy() - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "unicode sort, kind=%s" % kind c = a.copy() c.sort(kind=kind) @@ -1747,7 +1899,7 @@ class TestMethods(object): return True a = np.array([Boom()]*100, dtype=object) - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "bogus comparison object sort, kind=%s" % kind c.sort(kind=kind) @@ -1767,7 +1919,7 @@ class TestMethods(object): def test_sort_raises(self): #gh-9404 arr = np.array([0, datetime.now(), 1], dtype=object) - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: assert_raises(TypeError, arr.sort, kind=kind) #gh-3879 class Raiser(object): @@ -1776,7 +1928,7 @@ class TestMethods(object): __eq__ = __ne__ = __lt__ = __gt__ = __ge__ = __le__ = raises_anything arr = np.array([[Raiser(), n] for n in range(10)]).reshape(-1) np.random.shuffle(arr) - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: assert_raises(TypeError, arr.sort, kind=kind) def test_sort_degraded(self): @@ -1862,24 +2014,26 @@ class TestMethods(object): # of sorted items must be greater than ~50 to check the actual # algorithm because quick and merge sort fall over to insertion # sort for small arrays. - a = np.arange(101) - b = a[::-1].copy() - for kind in ['q', 'm', 'h']: - msg = "scalar argsort, kind=%s" % kind - assert_equal(a.copy().argsort(kind=kind), a, msg) - assert_equal(b.copy().argsort(kind=kind), b, msg) + + for dtype in [np.int32, np.uint32, np.float32]: + a = np.arange(101, dtype=dtype) + b = a[::-1].copy() + for kind in self.sort_kinds: + msg = "scalar argsort, kind=%s, dtype=%s" % (kind, dtype) + assert_equal(a.copy().argsort(kind=kind), a, msg) + assert_equal(b.copy().argsort(kind=kind), b, msg) # test complex argsorts. These use the same code as the scalars # but the compare function differs. ai = a*1j + 1 bi = b*1j + 1 - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "complex argsort, kind=%s" % kind assert_equal(ai.copy().argsort(kind=kind), a, msg) assert_equal(bi.copy().argsort(kind=kind), b, msg) ai = a + 1j bi = b + 1j - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "complex argsort, kind=%s" % kind assert_equal(ai.copy().argsort(kind=kind), a, msg) assert_equal(bi.copy().argsort(kind=kind), b, msg) @@ -1898,7 +2052,7 @@ class TestMethods(object): b = a[::-1].copy() r = np.arange(101) rr = r[::-1] - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "string argsort, kind=%s" % kind assert_equal(a.copy().argsort(kind=kind), r, msg) assert_equal(b.copy().argsort(kind=kind), rr, msg) @@ -1909,7 +2063,7 @@ class TestMethods(object): b = a[::-1] r = np.arange(101) rr = r[::-1] - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "unicode argsort, kind=%s" % kind assert_equal(a.copy().argsort(kind=kind), r, msg) assert_equal(b.copy().argsort(kind=kind), rr, msg) @@ -1920,7 +2074,7 @@ class TestMethods(object): b = a[::-1] r = np.arange(101) rr = r[::-1] - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "object argsort, kind=%s" % kind assert_equal(a.copy().argsort(kind=kind), r, msg) assert_equal(b.copy().argsort(kind=kind), rr, msg) @@ -1931,7 +2085,7 @@ class TestMethods(object): b = a[::-1] r = np.arange(101) rr = r[::-1] - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "structured array argsort, kind=%s" % kind assert_equal(a.copy().argsort(kind=kind), r, msg) assert_equal(b.copy().argsort(kind=kind), rr, msg) @@ -2849,8 +3003,6 @@ class TestMethods(object): assert_equal(b.diagonal(0, 2, 1), [[0, 3], [4, 7]]) 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) @@ -3124,8 +3276,8 @@ class TestMethods(object): assert_equal(ac, np.conjugate(a)) a = np.array([1-1j, 1, 2.0, 'f'], object) - assert_raises(AttributeError, lambda: a.conj()) - assert_raises(AttributeError, lambda: a.conjugate()) + assert_raises(TypeError, lambda: a.conj()) + assert_raises(TypeError, lambda: a.conjugate()) def test__complex__(self): dtypes = ['i1', 'i2', 'i4', 'i8', @@ -3666,17 +3818,6 @@ class TestSubscripting(object): class TestPickling(object): - def test_highest_available_pickle_protocol(self): - try: - import pickle5 - except ImportError: - pickle5 = None - - if sys.version_info[:2] >= (3, 8) or pickle5 is not None: - assert pickle.HIGHEST_PROTOCOL >= 5 - else: - assert pickle.HIGHEST_PROTOCOL < 5 - @pytest.mark.skipif(pickle.HIGHEST_PROTOCOL >= 5, reason=('this tests the error messages when trying to' 'protocol 5 although it is not available')) @@ -3759,10 +3900,16 @@ class TestPickling(object): ('c', float)]) ] + refs = [weakref.ref(a) for a in DATA] for a in DATA: assert_equal( a, pickle.loads(pickle.dumps(a, protocol=proto)), err_msg="%r" % a) + del a, DATA, carray + break_cycles() + # check for reference leaks (gh-12793) + for ref in refs: + assert ref() is None def _loads(self, obj): if sys.version_info[0] >= 3: @@ -3815,6 +3962,17 @@ class TestPickling(object): p = self._loads(s) assert_equal(a, p) + def test_datetime64_byteorder(self): + original = np.array([['2015-02-24T00:00:00.000000000']], dtype='datetime64[ns]') + + original_byte_reversed = original.copy(order='K') + original_byte_reversed.dtype = original_byte_reversed.dtype.newbyteorder('S') + original_byte_reversed.byteswap(inplace=True) + + new = pickle.loads(pickle.dumps(original_byte_reversed)) + + assert_equal(original.dtype, new.dtype) + class TestFancyIndexing(object): def test_list(self): @@ -4245,7 +4403,11 @@ class TestClip(object): x = (np.random.random(1000) * array_max).astype(dtype) if inplace: - x.clip(clip_min, clip_max, x) + # The tests that call us pass clip_min and clip_max that + # might not fit in the destination dtype. They were written + # assuming the previous unsafe casting, which now must be + # passed explicitly to avoid a warning. + x.clip(clip_min, clip_max, x, casting='unsafe') else: x = x.clip(clip_min, clip_max) byteorder = '=' @@ -4264,7 +4426,7 @@ class TestClip(object): 'float', 1024, 0, 0, inplace=inplace) self._clip_type( - 'int', 1024, -120, 100.5, inplace=inplace) + 'int', 1024, -120, 100, inplace=inplace) self._clip_type( 'int', 1024, 0, 0, inplace=inplace) @@ -4358,6 +4520,16 @@ class TestPutmask(object): assert_array_equal(rec['y'], [11, 4]) assert_array_equal(rec['z'], [3, 3]) + def test_overlaps(self): + # gh-6272 check overlap + x = np.array([True, False, True, False]) + np.putmask(x[1:4], [True, True, True], x[:3]) + assert_equal(x, np.array([True, True, False, True])) + + x = np.array([True, False, True, False]) + np.putmask(x[1:4], x[:3], [True, False, True]) + assert_equal(x, np.array([True, True, True, True])) + class TestTake(object): def tst_basic(self, x): @@ -4406,6 +4578,11 @@ class TestTake(object): rec1 = rec.take([1]) assert_(rec1['x'] == 5.0 and rec1['y'] == 4.0) + def test_out_overlap(self): + # gh-6272 check overlap on out + x = np.arange(5) + y = np.take(x, [1, 2, 3], out=x[2:5], mode='wrap') + assert_equal(y, np.array([1, 2, 3])) class TestLexsort(object): def test_basic(self): @@ -4521,6 +4698,20 @@ class TestIO(object): y = np.fromfile(self.filename, dtype=self.dtype) assert_array_equal(y, self.x.flat) + @pytest.mark.skipif(pathlib is None, reason="pathlib not found") + def test_roundtrip_pathlib(self): + p = pathlib.Path(self.filename) + self.x.tofile(p) + y = np.fromfile(p, dtype=self.dtype) + assert_array_equal(y, self.x.flat) + + @pytest.mark.skipif(pathlib is None, reason="pathlib not found") + def test_roundtrip_dump_pathlib(self): + p = pathlib.Path(self.filename) + self.x.dump(p) + y = np.load(p, allow_pickle=True) + assert_array_equal(y, self.x) + def test_roundtrip_binary_str(self): s = self.x.tobytes() y = np.frombuffer(s, dtype=self.dtype) @@ -4653,6 +4844,36 @@ class TestIO(object): assert_raises_regex(ValueError, "Cannot read into object array", np.fromfile, self.filename, dtype=object) + def test_fromfile_offset(self): + with open(self.filename, 'wb') as f: + self.x.tofile(f) + + with open(self.filename, 'rb') as f: + y = np.fromfile(f, dtype=self.dtype, offset=0) + assert_array_equal(y, self.x.flat) + + with open(self.filename, 'rb') as f: + count_items = len(self.x.flat) // 8 + offset_items = len(self.x.flat) // 4 + offset_bytes = self.dtype.itemsize * offset_items + y = np.fromfile(f, dtype=self.dtype, count=count_items, offset=offset_bytes) + assert_array_equal(y, self.x.flat[offset_items:offset_items+count_items]) + + # subsequent seeks should stack + offset_bytes = self.dtype.itemsize + z = np.fromfile(f, dtype=self.dtype, offset=offset_bytes) + assert_array_equal(z, self.x.flat[offset_items+count_items+1:]) + + with open(self.filename, 'wb') as f: + self.x.tofile(f, sep=",") + + with open(self.filename, 'rb') as f: + assert_raises_regex( + TypeError, + "'offset' argument only permitted for binary files", + np.fromfile, self.filename, dtype=self.dtype, + sep=",", offset=1) + def _check_from(self, s, value, **kw): if 'sep' not in kw: y = np.frombuffer(s, **kw) @@ -4854,6 +5075,22 @@ class TestFlat(object): assert_(e.flags.writebackifcopy is False) assert_(f.flags.writebackifcopy is False) + @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") + def test_refcount(self): + # includes regression test for reference count error gh-13165 + inds = [np.intp(0), np.array([True]*self.a.size), np.array([0]), None] + indtype = np.dtype(np.intp) + rc_indtype = sys.getrefcount(indtype) + for ind in inds: + rc_ind = sys.getrefcount(ind) + for _ in range(100): + try: + self.a.flat[ind] + except IndexError: + pass + assert_(abs(sys.getrefcount(ind) - rc_ind) < 50) + assert_(abs(sys.getrefcount(indtype) - rc_indtype) < 50) + class TestResize(object): def test_basic(self): @@ -5679,7 +5916,7 @@ class MatmulCommon(object): """ # Should work with these types. Will want to add # "O" at some point - types = "?bhilqBHILQefdgFDG" + types = "?bhilqBHILQefdgFDGO" def test_exceptions(self): dims = [ @@ -5730,8 +5967,9 @@ class MatmulCommon(object): assert_(res.dtype == dt) # vector vector returns scalars - res = self.matmul(v, v) - assert_(type(res) is np.dtype(dt).type) + if dt != "O": + res = self.matmul(v, v) + assert_(type(res) is np.dtype(dt).type) def test_scalar_output(self): vec1 = np.array([2]) @@ -5910,7 +6148,7 @@ class TestMatmul(MatmulCommon): assert_array_equal(out, tgt, err_msg=msg) # test out with not allowed type cast (safe casting) - msg = "Cannot cast ufunc matmul output" + msg = "Cannot cast ufunc .* output" out = np.zeros((5, 2), dtype=np.int32) assert_raises_regex(TypeError, msg, self.matmul, a, b, out=out) @@ -5982,7 +6220,52 @@ class TestMatmul(MatmulCommon): r3 = np.matmul(args[0].copy(), args[1].copy()) assert_equal(r1, r3) - + + def test_matmul_object(self): + import fractions + + f = np.vectorize(fractions.Fraction) + def random_ints(): + return np.random.randint(1, 1000, size=(10, 3, 3)) + M1 = f(random_ints(), random_ints()) + M2 = f(random_ints(), random_ints()) + + M3 = self.matmul(M1, M2) + + [N1, N2, N3] = [a.astype(float) for a in [M1, M2, M3]] + + assert_allclose(N3, self.matmul(N1, N2)) + + def test_matmul_object_type_scalar(self): + from fractions import Fraction as F + v = np.array([F(2,3), F(5,7)]) + res = self.matmul(v, v) + assert_(type(res) is F) + + def test_matmul_empty(self): + a = np.empty((3, 0), dtype=object) + b = np.empty((0, 3), dtype=object) + c = np.zeros((3, 3)) + assert_array_equal(np.matmul(a, b), c) + + def test_matmul_exception_multiply(self): + # test that matmul fails if `__mul__` is missing + class add_not_multiply(): + def __add__(self, other): + return self + a = np.full((3,3), add_not_multiply()) + with assert_raises(TypeError): + b = np.matmul(a, a) + + def test_matmul_exception_add(self): + # test that matmul fails if `__add__` is missing + class multiply_not_add(): + def __mul__(self, other): + return self + a = np.full((3,3), multiply_not_add()) + with assert_raises(TypeError): + b = np.matmul(a, a) + if sys.version_info[:2] >= (3, 5): @@ -6999,12 +7282,11 @@ class TestArrayAttributeDeletion(object): assert_raises(AttributeError, delattr, a, s) -def test_array_interface(): - # Test scalar coercion within the array interface +class TestArrayInterface(): class Foo(object): def __init__(self, value): self.value = value - self.iface = {'typestr': '=f8'} + self.iface = {'typestr': 'f8'} def __float__(self): return float(self.value) @@ -7013,22 +7295,39 @@ def test_array_interface(): def __array_interface__(self): return self.iface + f = Foo(0.5) - assert_equal(np.array(f), 0.5) - assert_equal(np.array([f]), [0.5]) - assert_equal(np.array([f, f]), [0.5, 0.5]) - assert_equal(np.array(f).dtype, np.dtype('=f8')) - # Test various shape definitions - f.iface['shape'] = () - assert_equal(np.array(f), 0.5) - f.iface['shape'] = None - assert_raises(TypeError, np.array, f) - f.iface['shape'] = (1, 1) - assert_equal(np.array(f), [[0.5]]) - f.iface['shape'] = (2,) - assert_raises(ValueError, np.array, f) - - # test scalar with no shape + + @pytest.mark.parametrize('val, iface, expected', [ + (f, {}, 0.5), + ([f], {}, [0.5]), + ([f, f], {}, [0.5, 0.5]), + (f, {'shape': ()}, 0.5), + (f, {'shape': None}, TypeError), + (f, {'shape': (1, 1)}, [[0.5]]), + (f, {'shape': (2,)}, ValueError), + (f, {'strides': ()}, 0.5), + (f, {'strides': (2,)}, ValueError), + (f, {'strides': 16}, TypeError), + ]) + def test_scalar_interface(self, val, iface, expected): + # Test scalar coercion within the array interface + self.f.iface = {'typestr': 'f8'} + self.f.iface.update(iface) + if HAS_REFCOUNT: + pre_cnt = sys.getrefcount(np.dtype('f8')) + if isinstance(expected, type): + assert_raises(expected, np.array, val) + else: + result = np.array(val) + assert_equal(np.array(val), expected) + assert result.dtype == 'f8' + del result + if HAS_REFCOUNT: + post_cnt = sys.getrefcount(np.dtype('f8')) + assert_equal(pre_cnt, post_cnt) + +def test_interface_no_shape(): class ArrayLike(object): array = np.array(1) __array_interface__ = array.__array_interface__ @@ -7070,6 +7369,19 @@ def test_array_interface_empty_shape(): assert_equal(arr1, arr2) assert_equal(arr1, arr3) +def test_array_interface_offset(): + arr = np.array([1, 2, 3], dtype='int32') + interface = dict(arr.__array_interface__) + interface['data'] = memoryview(arr) + interface['shape'] = (2,) + interface['offset'] = 4 + + + class DummyArray(object): + __array_interface__ = interface + + arr1 = np.asarray(DummyArray()) + assert_equal(arr1, arr[1:]) def test_flat_element_deletion(): it = np.ones(3).flat @@ -7096,7 +7408,7 @@ class TestMemEventHook(object): # needs to be larger then limit of small memory cacher in ctors.c a = np.zeros(1000) del a - gc.collect() + break_cycles() _multiarray_tests.test_pydatamem_seteventhook_end() class TestMapIter(object): @@ -7201,6 +7513,7 @@ class TestConversion(object): except NameError: Error = RuntimeError # python < 3.5 assert_raises(Error, bool, self_containing) # previously stack overflow + self_containing[0] = None # resolve circular reference def test_to_int_scalar(self): # gh-9972 means that these aren't always the same @@ -7626,6 +7939,55 @@ 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.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)), + _make_readonly(np.array([1, 2, 3])), + ], ids=[ + '1d', + '2d', + 'structured', + 'overlapping', + '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 + break_cycles() + 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 + break_cycles() + assert_(arr_ref() is None, "unknowable whether ctypes pointer holds a reference") + class TestWritebackIfCopy(object): # all these tests use the WRITEBACKIFCOPY mechanism @@ -7641,18 +8003,13 @@ class TestWritebackIfCopy(object): res = np.argmin(mat, 0, out=out) assert_equal(res, range(5)) - def test_clip_with_out(self): - mat = np.eye(5) - out = np.eye(5, dtype='i2') - res = np.clip(mat, a_min=-10, a_max=0, out=out) - assert_(res is out) - assert_equal(np.sum(out), 0) - def test_insert_noncontiguous(self): a = np.arange(6).reshape(2,3).T # force non-c-contiguous # uses arr_insert np.place(a, a>2, [44, 55]) assert_equal(a, np.array([[0, 44], [1, 55], [2, 44]])) + # hit one of the failing paths + assert_raises(ValueError, np.place, a, a>20, []) def test_put_noncontiguous(self): a = np.arange(6).reshape(2,3).T # force non-c-contiguous @@ -7803,15 +8160,15 @@ class TestArrayFinalize(object): assert_(isinstance(obj_subarray, RaisesInFinalize)) # reference should still be held by obj_arr - gc.collect() + break_cycles() assert_(obj_ref() is not None, "object should not already be dead") del obj_arr - gc.collect() + break_cycles() assert_(obj_ref() is not None, "obj_arr should not hold the last reference") del obj_subarray - gc.collect() + break_cycles() assert_(obj_ref() is None, "no references should remain") @@ -7923,6 +8280,77 @@ def test_uintalignment_and_alignment(): dst = np.zeros((2,2), dtype='c8') dst[:,1] = src[:,1] # assert in lowlevel_strided_loops fails? +class TestAlignment(object): + # adapted from scipy._lib.tests.test__util.test__aligned_zeros + # Checks that unusual memory alignments don't trip up numpy. + # In particular, check RELAXED_STRIDES don't trip alignment assertions in + # NDEBUG mode for size-0 arrays (gh-12503) + + def check(self, shape, dtype, order, align): + err_msg = repr((shape, dtype, order, align)) + x = _aligned_zeros(shape, dtype, order, align=align) + if align is None: + align = np.dtype(dtype).alignment + assert_equal(x.__array_interface__['data'][0] % align, 0) + if hasattr(shape, '__len__'): + assert_equal(x.shape, shape, err_msg) + else: + assert_equal(x.shape, (shape,), err_msg) + assert_equal(x.dtype, dtype) + if order == "C": + assert_(x.flags.c_contiguous, err_msg) + elif order == "F": + if x.size > 0: + assert_(x.flags.f_contiguous, err_msg) + elif order is None: + assert_(x.flags.c_contiguous, err_msg) + else: + raise ValueError() + + def test_various_alignments(self): + for align in [1, 2, 3, 4, 8, 12, 16, 32, 64, None]: + for n in [0, 1, 3, 11]: + for order in ["C", "F", None]: + for dtype in list(np.typecodes["All"]) + ['i4,i4,i4']: + if dtype == 'O': + # object dtype can't be misaligned + continue + for shape in [n, (1, 2, 3, n)]: + self.check(shape, np.dtype(dtype), order, align) + + def test_strided_loop_alignments(self): + # particularly test that complex64 and float128 use right alignment + # code-paths, since these are particularly problematic. It is useful to + # turn on USE_DEBUG for this test, so lowlevel-loop asserts are run. + for align in [1, 2, 4, 8, 12, 16, None]: + xf64 = _aligned_zeros(3, np.float64) + + xc64 = _aligned_zeros(3, np.complex64, align=align) + xf128 = _aligned_zeros(3, np.longdouble, align=align) + + # test casting, both to and from misaligned + with suppress_warnings() as sup: + sup.filter(np.ComplexWarning, "Casting complex values") + xc64.astype('f8') + xf64.astype(np.complex64) + test = xc64 + xf64 + + xf128.astype('f8') + xf64.astype(np.longdouble) + test = xf128 + xf64 + + test = xf128 + xc64 + + # test copy, both to and from misaligned + # contig copy + xf64[:] = xf64.copy() + xc64[:] = xc64.copy() + xf128[:] = xf128.copy() + # strided copy + xf64[::2] = xf64[::2].copy() + xc64[::2] = xc64[::2].copy() + xf128[::2] = xf128[::2].copy() + def test_getfield(): a = np.arange(32, dtype='uint16') if sys.byteorder == 'little': diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py index 26fd9c346..cf66751f8 100644 --- a/numpy/core/tests/test_nditer.py +++ b/numpy/core/tests/test_nditer.py @@ -1864,7 +1864,7 @@ def test_iter_buffered_cast_structured_type(): # make sure multi-field struct type -> simple doesn't work sdt = [('a', 'f4'), ('b', 'i8'), ('d', 'O')] a = np.array([(5.5, 7, 'test'), (8, 10, 11)], dtype=sdt) - assert_raises(ValueError, lambda: ( + assert_raises(TypeError, lambda: ( nditer(a, ['buffered', 'refs_ok'], ['readonly'], casting='unsafe', op_dtypes='i4'))) @@ -2292,7 +2292,7 @@ class TestIterNested(object): assert_equal(vals, [[0, 1, 2], [3, 4, 5]]) vals = None - # writebackifcopy - using conext manager + # writebackifcopy - using context manager a = arange(6, dtype='f4').reshape(2, 3) i, j = np.nested_iters(a, [[0], [1]], op_flags=['readwrite', 'updateifcopy'], diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 37534720a..935b84234 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -13,7 +13,7 @@ from numpy.random import rand, randint, randn from numpy.testing import ( assert_, assert_equal, assert_raises, assert_raises_regex, assert_array_equal, assert_almost_equal, assert_array_almost_equal, - HAS_REFCOUNT + assert_warns, HAS_REFCOUNT ) @@ -43,7 +43,7 @@ class TestResize(object): def test_reshape_from_zero(self): # See also gh-6740 - A = np.zeros(0, dtype=[('a', np.float32, 1)]) + A = np.zeros(0, dtype=[('a', np.float32)]) Ar = np.resize(A, (2, 1)) assert_array_equal(Ar, np.zeros((2, 1), Ar.dtype)) assert_equal(A.dtype, Ar.dtype) @@ -152,7 +152,15 @@ class TestNonarrayArgs(object): def test_squeeze(self): A = [[[1, 1, 1], [2, 2, 2], [3, 3, 3]]] - assert_(np.squeeze(A).shape == (3, 3)) + assert_equal(np.squeeze(A).shape, (3, 3)) + assert_equal(np.squeeze(np.zeros((1, 3, 1))).shape, (3,)) + assert_equal(np.squeeze(np.zeros((1, 3, 1)), axis=0).shape, (3, 1)) + assert_equal(np.squeeze(np.zeros((1, 3, 1)), axis=-1).shape, (1, 3)) + assert_equal(np.squeeze(np.zeros((1, 3, 1)), axis=2).shape, (1, 3)) + assert_equal(np.squeeze([np.zeros((3, 1))]).shape, (3,)) + assert_equal(np.squeeze([np.zeros((3, 1))], axis=0).shape, (3, 1)) + assert_equal(np.squeeze([np.zeros((3, 1))], axis=2).shape, (1, 3)) + assert_equal(np.squeeze([np.zeros((3, 1))], axis=-1).shape, (1, 3)) def test_std(self): A = [[1, 2, 3], [4, 5, 6]] @@ -208,6 +216,9 @@ class TestNonarrayArgs(object): assert_(np.isnan(np.var([]))) assert_(w[0].category is RuntimeWarning) + B = np.array([None, 0]) + B[0] = 1j + assert_almost_equal(np.var(B), 0.25) class TestIsscalar(object): def test_isscalar(self): @@ -888,6 +899,41 @@ class TestTypes(object): # Also test keyword arguments assert_(np.can_cast(from_=np.int32, to=np.int64)) + def test_can_cast_simple_to_structured(self): + # Non-structured can only be cast to structured in 'unsafe' mode. + assert_(not np.can_cast('i4', 'i4,i4')) + assert_(not np.can_cast('i4', 'i4,i2')) + assert_(np.can_cast('i4', 'i4,i4', casting='unsafe')) + assert_(np.can_cast('i4', 'i4,i2', casting='unsafe')) + # Even if there is just a single field which is OK. + assert_(not np.can_cast('i2', [('f1', 'i4')])) + assert_(not np.can_cast('i2', [('f1', 'i4')], casting='same_kind')) + assert_(np.can_cast('i2', [('f1', 'i4')], casting='unsafe')) + # It should be the same for recursive structured or subarrays. + assert_(not np.can_cast('i2', [('f1', 'i4,i4')])) + assert_(np.can_cast('i2', [('f1', 'i4,i4')], casting='unsafe')) + assert_(not np.can_cast('i2', [('f1', '(2,3)i4')])) + assert_(np.can_cast('i2', [('f1', '(2,3)i4')], casting='unsafe')) + + def test_can_cast_structured_to_simple(self): + # Need unsafe casting for structured to simple. + assert_(not np.can_cast([('f1', 'i4')], 'i4')) + assert_(np.can_cast([('f1', 'i4')], 'i4', casting='unsafe')) + assert_(np.can_cast([('f1', 'i4')], 'i2', casting='unsafe')) + # Since it is unclear what is being cast, multiple fields to + # single should not work even for unsafe casting. + assert_(not np.can_cast('i4,i4', 'i4', casting='unsafe')) + # But a single field inside a single field is OK. + assert_(not np.can_cast([('f1', [('x', 'i4')])], 'i4')) + assert_(np.can_cast([('f1', [('x', 'i4')])], 'i4', casting='unsafe')) + # And a subarray is fine too - it will just take the first element + # (arguably not very consistently; might also take the first field). + assert_(not np.can_cast([('f0', '(3,)i4')], 'i4')) + assert_(np.can_cast([('f0', '(3,)i4')], 'i4', casting='unsafe')) + # But a structured subarray with multiple fields should fail. + assert_(not np.can_cast([('f0', ('i4,i4'), (2,))], 'i4', + casting='unsafe')) + def test_can_cast_values(self): # gh-5917 for dt in np.sctypes['int'] + np.sctypes['uint']: @@ -965,12 +1011,24 @@ class TestNonzero(object): assert_equal(np.count_nonzero(np.array([], dtype='?')), 0) assert_equal(np.nonzero(np.array([])), ([],)) + assert_equal(np.count_nonzero(np.array([0])), 0) + assert_equal(np.count_nonzero(np.array([0], dtype='?')), 0) + assert_equal(np.nonzero(np.array([0])), ([],)) + + assert_equal(np.count_nonzero(np.array([1])), 1) + assert_equal(np.count_nonzero(np.array([1], dtype='?')), 1) + assert_equal(np.nonzero(np.array([1])), ([0],)) + + def test_nonzero_zerod(self): assert_equal(np.count_nonzero(np.array(0)), 0) assert_equal(np.count_nonzero(np.array(0, dtype='?')), 0) - assert_equal(np.nonzero(np.array(0)), ([],)) + with assert_warns(DeprecationWarning): + assert_equal(np.nonzero(np.array(0)), ([],)) + assert_equal(np.count_nonzero(np.array(1)), 1) assert_equal(np.count_nonzero(np.array(1, dtype='?')), 1) - assert_equal(np.nonzero(np.array(1)), ([0],)) + with assert_warns(DeprecationWarning): + assert_equal(np.nonzero(np.array(1)), ([0],)) def test_nonzero_onedim(self): x = np.array([1, 0, 2, -1, 0, 0, 8]) @@ -1164,6 +1222,38 @@ class TestNonzero(object): assert_raises(ValueError, np.nonzero, np.array([BoolErrors()])) + def test_nonzero_sideeffect_safety(self): + # gh-13631 + class FalseThenTrue: + _val = False + def __bool__(self): + try: + return self._val + finally: + self._val = True + + class TrueThenFalse: + _val = True + def __bool__(self): + try: + return self._val + finally: + self._val = False + + # result grows on the second pass + a = np.array([True, FalseThenTrue()]) + assert_raises(RuntimeError, np.nonzero, a) + + a = np.array([[True], [FalseThenTrue()]]) + assert_raises(RuntimeError, np.nonzero, a) + + # result shrinks on the second pass + a = np.array([False, TrueThenFalse()]) + assert_raises(RuntimeError, np.nonzero, a) + + a = np.array([[False], [TrueThenFalse()]]) + assert_raises(RuntimeError, np.nonzero, a) + class TestIndex(object): def test_boolean(self): @@ -1325,11 +1415,17 @@ class TestClip(object): self.nr = 5 self.nc = 3 - def fastclip(self, a, m, M, out=None): + def fastclip(self, a, m, M, out=None, casting=None): if out is None: - return a.clip(m, M) + if casting is None: + return a.clip(m, M) + else: + return a.clip(m, M, casting=casting) else: - return a.clip(m, M, out) + if casting is None: + return a.clip(m, M, out) + else: + return a.clip(m, M, out, casting=casting) def clip(self, a, m, M, out=None): # use slow-clip @@ -1367,6 +1463,20 @@ class TestClip(object): return (10 * rand(n, m)).astype(np.int32) # Now the real test cases + + @pytest.mark.parametrize("dtype", '?bhilqpBHILQPefdgFDGO') + def test_ones_pathological(self, dtype): + # for preservation of behavior described in + # gh-12519; amin > amax behavior may still change + # in the future + arr = np.ones(10, dtype=dtype) + expected = np.zeros(10, dtype=dtype) + actual = np.clip(arr, 1, 0) + if dtype == 'O': + assert actual.tolist() == expected.tolist() + else: + assert_equal(actual, expected) + def test_simple_double(self): # Test native double input with scalar min/max. a = self._generate_data(self.nr, self.nc) @@ -1465,14 +1575,21 @@ class TestClip(object): self.clip(a, m, M, act) assert_array_strict_equal(ac, act) - def test_simple_int32_inout(self): + @pytest.mark.parametrize("casting", [None, "unsafe"]) + def test_simple_int32_inout(self, casting): # Test native int32 input with double min/max and int32 out. a = self._generate_int32_data(self.nr, self.nc) m = np.float64(0) M = np.float64(2) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + if casting is None: + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac, casting=casting) + else: + # explicitly passing "unsafe" will silence warning + self.fastclip(a, m, M, ac, casting=casting) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1494,7 +1611,9 @@ class TestClip(object): M = np.float64(1) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1505,7 +1624,9 @@ class TestClip(object): M = 2.0 ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1681,7 +1802,9 @@ class TestClip(object): M = np.float64(2) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1703,7 +1826,9 @@ class TestClip(object): M = np.float64(1) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1714,7 +1839,9 @@ class TestClip(object): M = 2.0 ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1767,11 +1894,94 @@ class TestClip(object): def test_clip_nan(self): d = np.arange(7.) - assert_equal(d.clip(min=np.nan), d) - assert_equal(d.clip(max=np.nan), d) - assert_equal(d.clip(min=np.nan, max=np.nan), d) - assert_equal(d.clip(min=-2, max=np.nan), d) - assert_equal(d.clip(min=np.nan, max=10), d) + with assert_warns(DeprecationWarning): + assert_equal(d.clip(min=np.nan), d) + with assert_warns(DeprecationWarning): + assert_equal(d.clip(max=np.nan), d) + with assert_warns(DeprecationWarning): + assert_equal(d.clip(min=np.nan, max=np.nan), d) + with assert_warns(DeprecationWarning): + assert_equal(d.clip(min=-2, max=np.nan), d) + with assert_warns(DeprecationWarning): + assert_equal(d.clip(min=np.nan, max=10), d) + + def test_object_clip(self): + a = np.arange(10, dtype=object) + actual = np.clip(a, 1, 5) + expected = np.array([1, 1, 2, 3, 4, 5, 5, 5, 5, 5]) + assert actual.tolist() == expected.tolist() + + def test_clip_all_none(self): + a = np.arange(10, dtype=object) + with assert_raises_regex(ValueError, 'max or min'): + np.clip(a, None, None) + + def test_clip_invalid_casting(self): + a = np.arange(10, dtype=object) + with assert_raises_regex(ValueError, + 'casting must be one of'): + self.fastclip(a, 1, 8, casting="garbage") + + @pytest.mark.parametrize("amin, amax", [ + # two scalars + (1, 0), + # mix scalar and array + (1, np.zeros(10)), + # two arrays + (np.ones(10), np.zeros(10)), + ]) + def test_clip_value_min_max_flip(self, amin, amax): + a = np.arange(10, dtype=np.int64) + # requirement from ufunc_docstrings.py + expected = np.minimum(np.maximum(a, amin), amax) + actual = np.clip(a, amin, amax) + assert_equal(actual, expected) + + @pytest.mark.parametrize("arr, amin, amax, exp", [ + # for a bug in npy_ObjectClip, based on a + # case produced by hypothesis + (np.zeros(10, dtype=np.int64), + 0, + -2**64+1, + np.full(10, -2**64+1, dtype=object)), + # for bugs in NPY_TIMEDELTA_MAX, based on a case + # produced by hypothesis + (np.zeros(10, dtype='m8') - 1, + 0, + 0, + np.zeros(10, dtype='m8')), + ]) + def test_clip_problem_cases(self, arr, amin, amax, exp): + actual = np.clip(arr, amin, amax) + assert_equal(actual, exp) + + @pytest.mark.xfail(reason="no scalar nan propagation yet") + @pytest.mark.parametrize("arr, amin, amax", [ + # problematic scalar nan case from hypothesis + (np.zeros(10, dtype=np.int64), + np.array(np.nan), + np.zeros(10, dtype=np.int32)), + ]) + def test_clip_scalar_nan_propagation(self, arr, amin, amax): + # enforcement of scalar nan propagation for comparisons + # called through clip() + expected = np.minimum(np.maximum(a, amin), amax) + with assert_warns(DeprecationWarning): + actual = np.clip(arr, amin, amax) + assert_equal(actual, expected) + + @pytest.mark.xfail(reason="propagation doesn't match spec") + @pytest.mark.parametrize("arr, amin, amax", [ + (np.array([1] * 10, dtype='m8'), + np.timedelta64('NaT'), + np.zeros(10, dtype=np.int32)), + ]) + def test_NaT_propagation(self, arr, amin, amax): + # NOTE: the expected function spec doesn't + # propagate NaT, but clip() now does + expected = np.minimum(np.maximum(a, amin), amax) + actual = np.clip(arr, amin, amax) + assert_equal(actual, expected) class TestAllclose(object): @@ -2146,6 +2356,7 @@ class TestLikeFuncs(object): (np.arange(24).reshape(2, 3, 4).swapaxes(0, 1), None), (np.arange(24).reshape(4, 3, 2).swapaxes(0, 1), '?'), ] + self.shapes = [(5,), (5,6,), (5,6,7,)] def compare_array_value(self, dz, value, fill_value): if value is not None: @@ -2211,6 +2422,34 @@ class TestLikeFuncs(object): assert_equal(dz.dtype, np.dtype(dtype)) self.compare_array_value(dz, value, fill_value) + # Test the 'shape' parameter + for s in self.shapes: + for o in 'CFA': + sz = like_function(d, dtype=dtype, shape=s, order=o, + **fill_kwarg) + assert_equal(sz.shape, s) + if dtype is None: + assert_equal(sz.dtype, d.dtype) + else: + assert_equal(sz.dtype, np.dtype(dtype)) + if o == 'C' or (o == 'A' and d.flags.c_contiguous): + assert_(sz.flags.c_contiguous) + elif o == 'F' or (o == 'A' and d.flags.f_contiguous): + assert_(sz.flags.f_contiguous) + self.compare_array_value(sz, value, fill_value) + + if (d.ndim != len(s)): + assert_equal(np.argsort(like_function(d, dtype=dtype, + shape=s, order='K', + **fill_kwarg).strides), + np.argsort(np.empty(s, dtype=dtype, + order='C').strides)) + else: + assert_equal(np.argsort(like_function(d, dtype=dtype, + shape=s, order='K', + **fill_kwarg).strides), + np.argsort(d.strides)) + # Test the 'subok' parameter class MyNDArray(np.ndarray): pass @@ -2616,6 +2855,47 @@ def test_outer_out_param(): assert_equal(np.outer(arr2, arr3, out2), out2) +class TestIndices(object): + + def test_simple(self): + [x, y] = np.indices((4, 3)) + assert_array_equal(x, np.array([[0, 0, 0], + [1, 1, 1], + [2, 2, 2], + [3, 3, 3]])) + assert_array_equal(y, np.array([[0, 1, 2], + [0, 1, 2], + [0, 1, 2], + [0, 1, 2]])) + + def test_single_input(self): + [x] = np.indices((4,)) + assert_array_equal(x, np.array([0, 1, 2, 3])) + + [x] = np.indices((4,), sparse=True) + assert_array_equal(x, np.array([0, 1, 2, 3])) + + def test_scalar_input(self): + assert_array_equal([], np.indices(())) + assert_array_equal([], np.indices((), sparse=True)) + assert_array_equal([[]], np.indices((0,))) + assert_array_equal([[]], np.indices((0,), sparse=True)) + + def test_sparse(self): + [x, y] = np.indices((4,3), sparse=True) + assert_array_equal(x, np.array([[0], [1], [2], [3]])) + assert_array_equal(y, np.array([[0, 1, 2]])) + + @pytest.mark.parametrize("dtype", [np.int, np.float32, np.float64]) + @pytest.mark.parametrize("dims", [(), (0,), (4, 3)]) + def test_return_type(self, dtype, dims): + inds = np.indices(dims, dtype=dtype) + assert_(inds.dtype == dtype) + + for arr in np.indices(dims, dtype=dtype, sparse=True): + assert_(arr.dtype == dtype) + + class TestRequire(object): flag_names = ['C', 'C_CONTIGUOUS', 'CONTIGUOUS', 'F', 'F_CONTIGUOUS', 'FORTRAN', @@ -2696,6 +2976,8 @@ class TestBroadcast(object): arrs = [np.empty((6, 7)), np.empty((5, 6, 1)), np.empty((7,)), np.empty((5, 1, 7))] mits = [np.broadcast(*arrs), + np.broadcast(np.broadcast(*arrs[:0]), np.broadcast(*arrs[0:])), + np.broadcast(np.broadcast(*arrs[:1]), np.broadcast(*arrs[1:])), np.broadcast(np.broadcast(*arrs[:2]), np.broadcast(*arrs[2:])), np.broadcast(arrs[0], np.broadcast(*arrs[1:-1]), arrs[-1])] for mit in mits: @@ -2720,12 +3002,24 @@ class TestBroadcast(object): arr = np.empty((5,)) for j in range(35): arrs = [arr] * j - if j < 1 or j > 32: + if j > 32: assert_raises(ValueError, np.broadcast, *arrs) else: mit = np.broadcast(*arrs) assert_equal(mit.numiter, j) + def test_broadcast_error_kwargs(self): + #gh-13455 + arrs = [np.empty((5, 6, 7))] + mit = np.broadcast(*arrs) + mit2 = np.broadcast(*arrs, **{}) + assert_equal(mit.shape, mit2.shape) + assert_equal(mit.ndim, mit2.ndim) + assert_equal(mit.nd, mit2.nd) + assert_equal(mit.numiter, mit2.numiter) + assert_(mit.iters[0].base is mit2.iters[0].base) + + assert_raises(ValueError, np.broadcast, 1, **{'x': 1}) class TestKeepdims(object): @@ -2748,3 +3042,9 @@ class TestTensordot(object): td = np.tensordot(a, b, (1, 0)) assert_array_equal(td, np.dot(a, b)) assert_array_equal(td, np.einsum('ij,jk', a, b)) + + def test_zero_dimensional(self): + # gh-12130 + arr_0d = np.array(1) + ret = np.tensordot(arr_0d, arr_0d, ([], [])) # contracting no axes is well defined + assert_array_equal(ret, arr_0d) diff --git a/numpy/core/tests/test_numerictypes.py b/numpy/core/tests/test_numerictypes.py index 71f7b7150..d0ff5578a 100644 --- a/numpy/core/tests/test_numerictypes.py +++ b/numpy/core/tests/test_numerictypes.py @@ -5,7 +5,7 @@ import itertools import pytest import numpy as np -from numpy.testing import assert_, assert_equal, assert_raises +from numpy.testing import assert_, assert_equal, assert_raises, IS_PYPY # This is the structure of the table used for plain objects: # @@ -491,6 +491,7 @@ def test_issctype(rep, expected): @pytest.mark.skipif(sys.flags.optimize > 1, reason="no docstrings present to inspect when PYTHONOPTIMIZE/Py_OptimizeFlag > 1") +@pytest.mark.xfail(IS_PYPY, reason="PyPy does not modify tp_doc") class TestDocStrings(object): def test_platform_dependent_aliases(self): if np.int64 is np.int_: diff --git a/numpy/core/tests/test_overrides.py b/numpy/core/tests/test_overrides.py index 62b2a3e53..63b0e4539 100644 --- a/numpy/core/tests/test_overrides.py +++ b/numpy/core/tests/test_overrides.py @@ -2,27 +2,23 @@ from __future__ import division, absolute_import, print_function import inspect import sys +from unittest import mock import numpy as np from numpy.testing import ( assert_, assert_equal, assert_raises, assert_raises_regex) from numpy.core.overrides import ( - get_overloaded_types_and_args, array_function_dispatch, - verify_matching_signatures, ENABLE_ARRAY_FUNCTION) -from numpy.core.numeric import pickle + _get_implementing_args, array_function_dispatch, + verify_matching_signatures, ARRAY_FUNCTION_ENABLED) +from numpy.compat import pickle import pytest requires_array_function = pytest.mark.skipif( - not ENABLE_ARRAY_FUNCTION, + not ARRAY_FUNCTION_ENABLED, reason="__array_function__ dispatch not enabled.") -def _get_overloaded_args(relevant_args): - types, args = get_overloaded_types_and_args(relevant_args) - return args - - def _return_not_implemented(self, *args, **kwargs): return NotImplemented @@ -40,27 +36,21 @@ def dispatched_two_arg(array1, array2): return 'original' -@requires_array_function -class TestGetOverloadedTypesAndArgs(object): +class TestGetImplementingArgs(object): def test_ndarray(self): array = np.array(1) - types, args = get_overloaded_types_and_args([array]) - assert_equal(set(types), {np.ndarray}) + args = _get_implementing_args([array]) assert_equal(list(args), [array]) - types, args = get_overloaded_types_and_args([array, array]) - assert_equal(len(types), 1) - assert_equal(set(types), {np.ndarray}) + args = _get_implementing_args([array, array]) assert_equal(list(args), [array]) - types, args = get_overloaded_types_and_args([array, 1]) - assert_equal(set(types), {np.ndarray}) + args = _get_implementing_args([array, 1]) assert_equal(list(args), [array]) - types, args = get_overloaded_types_and_args([1, array]) - assert_equal(set(types), {np.ndarray}) + args = _get_implementing_args([1, array]) assert_equal(list(args), [array]) def test_ndarray_subclasses(self): @@ -75,17 +65,14 @@ class TestGetOverloadedTypesAndArgs(object): override_sub = np.array(1).view(OverrideSub) no_override_sub = np.array(1).view(NoOverrideSub) - types, args = get_overloaded_types_and_args([array, override_sub]) - assert_equal(set(types), {np.ndarray, OverrideSub}) + args = _get_implementing_args([array, override_sub]) assert_equal(list(args), [override_sub, array]) - types, args = get_overloaded_types_and_args([array, no_override_sub]) - assert_equal(set(types), {np.ndarray, NoOverrideSub}) + args = _get_implementing_args([array, no_override_sub]) assert_equal(list(args), [no_override_sub, array]) - types, args = get_overloaded_types_and_args( + args = _get_implementing_args( [override_sub, no_override_sub]) - assert_equal(set(types), {OverrideSub, NoOverrideSub}) assert_equal(list(args), [override_sub, no_override_sub]) def test_ndarray_and_duck_array(self): @@ -96,12 +83,10 @@ class TestGetOverloadedTypesAndArgs(object): array = np.array(1) other = Other() - types, args = get_overloaded_types_and_args([other, array]) - assert_equal(set(types), {np.ndarray, Other}) + args = _get_implementing_args([other, array]) assert_equal(list(args), [other, array]) - types, args = get_overloaded_types_and_args([array, other]) - assert_equal(set(types), {np.ndarray, Other}) + args = _get_implementing_args([array, other]) assert_equal(list(args), [array, other]) def test_ndarray_subclass_and_duck_array(self): @@ -116,9 +101,9 @@ class TestGetOverloadedTypesAndArgs(object): subarray = np.array(1).view(OverrideSub) other = Other() - assert_equal(_get_overloaded_args([array, subarray, other]), + assert_equal(_get_implementing_args([array, subarray, other]), [subarray, array, other]) - assert_equal(_get_overloaded_args([array, other, subarray]), + assert_equal(_get_implementing_args([array, other, subarray]), [subarray, array, other]) def test_many_duck_arrays(self): @@ -140,20 +125,31 @@ class TestGetOverloadedTypesAndArgs(object): c = C() d = D() - assert_equal(_get_overloaded_args([1]), []) - assert_equal(_get_overloaded_args([a]), [a]) - assert_equal(_get_overloaded_args([a, 1]), [a]) - assert_equal(_get_overloaded_args([a, a, a]), [a]) - assert_equal(_get_overloaded_args([a, d, a]), [a, d]) - assert_equal(_get_overloaded_args([a, b]), [b, a]) - assert_equal(_get_overloaded_args([b, a]), [b, a]) - assert_equal(_get_overloaded_args([a, b, c]), [b, c, a]) - assert_equal(_get_overloaded_args([a, c, b]), [c, b, a]) + assert_equal(_get_implementing_args([1]), []) + assert_equal(_get_implementing_args([a]), [a]) + assert_equal(_get_implementing_args([a, 1]), [a]) + assert_equal(_get_implementing_args([a, a, a]), [a]) + assert_equal(_get_implementing_args([a, d, a]), [a, d]) + assert_equal(_get_implementing_args([a, b]), [b, a]) + assert_equal(_get_implementing_args([b, a]), [b, a]) + assert_equal(_get_implementing_args([a, b, c]), [b, c, a]) + assert_equal(_get_implementing_args([a, c, b]), [c, b, a]) + + def test_too_many_duck_arrays(self): + namespace = dict(__array_function__=_return_not_implemented) + types = [type('A' + str(i), (object,), namespace) for i in range(33)] + relevant_args = [t() for t in types] + + actual = _get_implementing_args(relevant_args[:32]) + assert_equal(actual, relevant_args[:32]) + + with assert_raises_regex(TypeError, 'distinct argument types'): + _get_implementing_args(relevant_args) -@requires_array_function class TestNDArrayArrayFunction(object): + @requires_array_function def test_method(self): class Other(object): @@ -201,6 +197,16 @@ class TestNDArrayArrayFunction(object): result = np.concatenate((array, override_sub)) assert_equal(result, expected.view(OverrideSub)) + def test_no_wrapper(self): + # This shouldn't happen unless a user intentionally calls + # __array_function__ with invalid arguments, but check that we raise + # an appropriate error all the same. + array = np.array(1) + func = lambda x: x + with assert_raises_regex(AttributeError, '_implementation'): + array.__array_function__(func=func, types=(np.ndarray,), + args=(array,), kwargs={}) + @requires_array_function class TestArrayFunctionDispatch(object): @@ -372,7 +378,6 @@ class TestNumPyFunctions(object): assert_equal(np.fft.fft.__module__, 'numpy.fft') assert_equal(np.linalg.solve.__module__, 'numpy.linalg') - @pytest.mark.skipif(sys.version_info[0] < 3, reason="Python 3 only") def test_inspect_sum(self): signature = inspect.signature(np.sum) assert_('axis' in signature.parameters) @@ -386,3 +391,39 @@ class TestNumPyFunctions(object): return 'yes' assert_equal(np.sum(MyArray()), 'yes') + + @requires_array_function + def test_sum_on_mock_array(self): + + # We need a proxy for mocks because __array_function__ is only looked + # up in the class dict + class ArrayProxy: + def __init__(self, value): + self.value = value + def __array_function__(self, *args, **kwargs): + return self.value.__array_function__(*args, **kwargs) + def __array__(self, *args, **kwargs): + return self.value.__array__(*args, **kwargs) + + proxy = ArrayProxy(mock.Mock(spec=ArrayProxy)) + proxy.value.__array_function__.return_value = 1 + result = np.sum(proxy) + assert_equal(result, 1) + proxy.value.__array_function__.assert_called_once_with( + np.sum, (ArrayProxy,), (proxy,), {}) + proxy.value.__array__.assert_not_called() + + @requires_array_function + def test_sum_forwarding_implementation(self): + + class MyArray(np.ndarray): + + def sum(self, axis, out): + return 'summed' + + def __array_function__(self, func, types, args, kwargs): + return super().__array_function__(func, types, args, kwargs) + + # note: the internal implementation of np.sum() calls the .sum() method + array = np.array(1).view(MyArray) + assert_equal(np.sum(array), 'summed') diff --git a/numpy/core/tests/test_records.py b/numpy/core/tests/test_records.py index c059ef510..14413224e 100644 --- a/numpy/core/tests/test_records.py +++ b/numpy/core/tests/test_records.py @@ -17,7 +17,7 @@ from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_array_almost_equal, assert_raises, temppath ) -from numpy.core.numeric import pickle +from numpy.compat import pickle class TestFromrecords(object): @@ -435,7 +435,14 @@ class TestRecord(object): def test_missing_field(self): # https://github.com/numpy/numpy/issues/4806 arr = np.zeros((3,), dtype=[('x', int), ('y', int)]) - assert_raises(ValueError, lambda: arr[['nofield']]) + assert_raises(KeyError, lambda: arr[['nofield']]) + + def test_fromarrays_nested_structured_arrays(self): + arrays = [ + np.arange(10), + np.ones(10, dtype=[('a', '<u2'), ('b', '<f4')]), + ] + arr = np.rec.fromarrays(arrays) # ValueError? def test_find_duplicate(): diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py index 2421a1161..e564ae300 100644 --- a/numpy/core/tests/test_regression.py +++ b/numpy/core/tests/test_regression.py @@ -16,8 +16,7 @@ from numpy.testing import ( assert_raises_regex, assert_warns, suppress_warnings, _assert_valid_refcount, HAS_REFCOUNT, ) -from numpy.compat import asbytes, asunicode, long -from numpy.core.numeric import pickle +from numpy.compat import asbytes, asunicode, long, pickle try: RecursionError @@ -46,7 +45,7 @@ class TestRegression(object): assert_array_equal(a, b) def test_typeNA(self): - # Issue gh-515 + # Issue gh-515 with suppress_warnings() as sup: sup.filter(np.VisibleDeprecationWarning) assert_equal(np.typeNA[np.int64], 'Int64') @@ -99,7 +98,7 @@ class TestRegression(object): f = BytesIO() pickle.dump(ca, f, protocol=proto) f.seek(0) - ca = np.load(f) + ca = np.load(f, allow_pickle=True) f.close() def test_noncontiguous_fill(self): @@ -2411,7 +2410,67 @@ class TestRegression(object): if HAS_REFCOUNT: assert_(base <= sys.getrefcount(s)) + @pytest.mark.parametrize('val', [ + # arrays and scalars + np.ones((10, 10), dtype='int32'), + np.uint64(10), + ]) + @pytest.mark.parametrize('protocol', + range(2, pickle.HIGHEST_PROTOCOL + 1) + ) + def test_pickle_module(self, protocol, val): + # gh-12837 + s = pickle.dumps(val, protocol) + assert b'_multiarray_umath' not in s + if protocol == 5 and len(val.shape) > 0: + # unpickling ndarray goes through _frombuffer for protocol 5 + assert b'numpy.core.numeric' in s + else: + assert b'numpy.core.multiarray' in s + def test_object_casting_errors(self): # gh-11993 arr = np.array(['AAAAA', 18465886.0, 18465886.0], dtype=object) assert_raises(TypeError, arr.astype, 'c8') + + def test_eff1d_casting(self): + # gh-12711 + x = np.array([1, 2, 4, 7, 0], dtype=np.int16) + res = np.ediff1d(x, to_begin=-99, to_end=np.array([88, 99])) + assert_equal(res, [-99, 1, 2, 3, -7, 88, 99]) + assert_raises(ValueError, np.ediff1d, x, to_begin=(1<<20)) + assert_raises(ValueError, np.ediff1d, x, to_end=(1<<20)) + + def test_pickle_datetime64_array(self): + # gh-12745 (would fail with pickle5 installed) + d = np.datetime64('2015-07-04 12:59:59.50', 'ns') + arr = np.array([d]) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + dumped = pickle.dumps(arr, protocol=proto) + assert_equal(pickle.loads(dumped), arr) + + def test_bad_array_interface(self): + class T(object): + __array_interface__ = {} + + np.array([T()]) + + def test_2d__array__shape(self): + class T(object): + def __array__(self): + return np.ndarray(shape=(0,0)) + + # Make sure __array__ is used instead of Sequence methods. + def __iter__(self): + return iter([]) + + def __getitem__(self, idx): + raise AssertionError("__getitem__ was called") + + def __len__(self): + return 0 + + + t = T() + #gh-13659, would raise in broadcasting [x=t for x in result] + np.array([t]) diff --git a/numpy/core/tests/test_scalar_methods.py b/numpy/core/tests/test_scalar_methods.py new file mode 100644 index 000000000..93434dd1b --- /dev/null +++ b/numpy/core/tests/test_scalar_methods.py @@ -0,0 +1,109 @@ +""" +Test the scalar constructors, which also do type-coercion +""" +from __future__ import division, absolute_import, print_function + +import os +import fractions +import platform + +import pytest +import numpy as np + +from numpy.testing import ( + run_module_suite, + assert_equal, assert_almost_equal, assert_raises, assert_warns, + dec +) + +class TestAsIntegerRatio(object): + # derived in part from the cpython test "test_floatasratio" + + @pytest.mark.parametrize("ftype", [ + np.half, np.single, np.double, np.longdouble]) + @pytest.mark.parametrize("f, ratio", [ + (0.875, (7, 8)), + (-0.875, (-7, 8)), + (0.0, (0, 1)), + (11.5, (23, 2)), + ]) + def test_small(self, ftype, f, ratio): + assert_equal(ftype(f).as_integer_ratio(), ratio) + + @pytest.mark.parametrize("ftype", [ + np.half, np.single, np.double, np.longdouble]) + def test_simple_fractions(self, ftype): + R = fractions.Fraction + assert_equal(R(0, 1), + R(*ftype(0.0).as_integer_ratio())) + assert_equal(R(5, 2), + R(*ftype(2.5).as_integer_ratio())) + assert_equal(R(1, 2), + R(*ftype(0.5).as_integer_ratio())) + assert_equal(R(-2100, 1), + R(*ftype(-2100.0).as_integer_ratio())) + + @pytest.mark.parametrize("ftype", [ + np.half, np.single, np.double, np.longdouble]) + def test_errors(self, ftype): + assert_raises(OverflowError, ftype('inf').as_integer_ratio) + assert_raises(OverflowError, ftype('-inf').as_integer_ratio) + assert_raises(ValueError, ftype('nan').as_integer_ratio) + + def test_against_known_values(self): + R = fractions.Fraction + assert_equal(R(1075, 512), + R(*np.half(2.1).as_integer_ratio())) + assert_equal(R(-1075, 512), + R(*np.half(-2.1).as_integer_ratio())) + assert_equal(R(4404019, 2097152), + R(*np.single(2.1).as_integer_ratio())) + assert_equal(R(-4404019, 2097152), + R(*np.single(-2.1).as_integer_ratio())) + assert_equal(R(4728779608739021, 2251799813685248), + R(*np.double(2.1).as_integer_ratio())) + assert_equal(R(-4728779608739021, 2251799813685248), + R(*np.double(-2.1).as_integer_ratio())) + # longdouble is platform dependent + + @pytest.mark.parametrize("ftype, frac_vals, exp_vals", [ + # dtype test cases generated using hypothesis + # first five generated cases per dtype + (np.half, [0.0, 0.01154830649280303, 0.31082276347447274, + 0.527350517124794, 0.8308562335072596], + [0, 1, 0, -8, 12]), + (np.single, [0.0, 0.09248576989263226, 0.8160498218131407, + 0.17389442853722373, 0.7956044195067877], + [0, 12, 10, 17, -26]), + (np.double, [0.0, 0.031066908499895136, 0.5214135908877832, + 0.45780736035689296, 0.5906586745934036], + [0, -801, 51, 194, -653]), + pytest.param( + np.longdouble, + [0.0, 0.20492557202724854, 0.4277180662199366, 0.9888085019891495, + 0.9620175814461964], + [0, -7400, 14266, -7822, -8721], + marks=[ + pytest.mark.skipif( + np.finfo(np.double) == np.finfo(np.longdouble), + reason="long double is same as double"), + pytest.mark.skipif( + platform.machine().startswith("ppc"), + reason="IBM double double"), + ] + ) + ]) + def test_roundtrip(self, ftype, frac_vals, exp_vals): + for frac, exp in zip(frac_vals, exp_vals): + f = np.ldexp(frac, exp, dtype=ftype) + n, d = f.as_integer_ratio() + + try: + # workaround for gh-9968 + nf = np.longdouble(str(n)) + df = np.longdouble(str(d)) + except (OverflowError, RuntimeWarning): + # the values may not fit in any float type + pytest.skip("longdouble too small on this platform") + + assert_equal(nf / df, f, "{}/{}".format(n, d)) diff --git a/numpy/core/tests/test_scalarbuffer.py b/numpy/core/tests/test_scalarbuffer.py index cd520d99b..3ded7eecd 100644 --- a/numpy/core/tests/test_scalarbuffer.py +++ b/numpy/core/tests/test_scalarbuffer.py @@ -65,7 +65,7 @@ class TestScalarPEP3118(object): assert_(isinstance(x, np.void)) mv_x = memoryview(x) expected_size = 16 * np.dtype((np.unicode_, 1)).itemsize - expected_size += 2 * np.dtype((np.float64, 1)).itemsize + expected_size += 2 * np.dtype(np.float64).itemsize assert_equal(mv_x.itemsize, expected_size) assert_equal(mv_x.ndim, 0) assert_equal(mv_x.shape, ()) diff --git a/numpy/core/tests/test_scalarmath.py b/numpy/core/tests/test_scalarmath.py index a7bb4b3c0..7a1771ed8 100644 --- a/numpy/core/tests/test_scalarmath.py +++ b/numpy/core/tests/test_scalarmath.py @@ -422,7 +422,7 @@ class TestConversion(object): @pytest.mark.skipif(np.finfo(np.double) == np.finfo(np.longdouble), reason="long double is same as double") - @pytest.mark.skipif(platform.machine().startswith("ppc64"), + @pytest.mark.skipif(platform.machine().startswith("ppc"), reason="IBM double double") def test_int_from_huge_longdouble(self): # Produce a longdouble that would overflow a double, diff --git a/numpy/core/tests/test_scalarprint.py b/numpy/core/tests/test_scalarprint.py index cde1355aa..86b0ca199 100644 --- a/numpy/core/tests/test_scalarprint.py +++ b/numpy/core/tests/test_scalarprint.py @@ -51,7 +51,7 @@ class TestRealScalars(object): def test_py2_float_print(self): # gh-10753 - # In python2, the python float type implements an obsolte method + # In python2, the python float type implements an obsolete method # tp_print, which overrides tp_repr and tp_str when using "print" to # output to a "real file" (ie, not a StringIO). Make sure we don't # inherit it. diff --git a/numpy/core/tests/test_shape_base.py b/numpy/core/tests/test_shape_base.py index ef5c118ec..53d272fc5 100644 --- a/numpy/core/tests/test_shape_base.py +++ b/numpy/core/tests/test_shape_base.py @@ -224,13 +224,27 @@ class TestConcatenate(object): assert_raises(ValueError, concatenate, (0,)) assert_raises(ValueError, concatenate, (np.array(0),)) + # dimensionality must match + assert_raises_regex( + ValueError, + r"all the input arrays must have same number of dimensions, but " + r"the array at index 0 has 1 dimension\(s\) and the array at " + r"index 1 has 2 dimension\(s\)", + np.concatenate, (np.zeros(1), np.zeros((1, 1)))) + # test shapes must match except for concatenation axis a = np.ones((1, 2, 3)) b = np.ones((2, 2, 3)) axis = list(range(3)) for i in range(3): np.concatenate((a, b), axis=axis[0]) # OK - assert_raises(ValueError, np.concatenate, (a, b), axis=axis[1]) + assert_raises_regex( + ValueError, + "all the input array dimensions for the concatenation axis " + "must match exactly, but along dimension {}, the array at " + "index 0 has size 1 and the array at index 1 has size 2" + .format(i), + np.concatenate, (a, b), axis=axis[1]) assert_raises(ValueError, np.concatenate, (a, b), axis=axis[2]) a = np.moveaxis(a, -1, 0) b = np.moveaxis(b, -1, 0) @@ -373,6 +387,10 @@ def test_stack(): # empty arrays assert_(stack([[], [], []]).shape == (3, 0)) assert_(stack([[], [], []], axis=1).shape == (0, 3)) + # out + out = np.zeros_like(r1) + np.stack((a, b), out=out) + assert_array_equal(out, r1) # edge cases assert_raises_regex(ValueError, 'need at least one array', stack, []) assert_raises_regex(ValueError, 'must have the same shape', diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index b83b8ccff..69fbc35e3 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -3,6 +3,8 @@ from __future__ import division, absolute_import, print_function import warnings import itertools +import pytest + import numpy as np import numpy.core._umath_tests as umt import numpy.linalg._umath_linalg as uml @@ -13,7 +15,7 @@ from numpy.testing import ( assert_almost_equal, assert_array_almost_equal, assert_no_warnings, assert_allclose, ) -from numpy.core.numeric import pickle +from numpy.compat import pickle class TestUfuncKwargs(object): @@ -42,6 +44,103 @@ class TestUfuncKwargs(object): assert_raises(TypeError, np.add, 1, 2, extobj=[4096], parrot=True) +class TestUfuncGenericLoops(object): + """Test generic loops. + + The loops to be tested are: + + PyUFunc_ff_f_As_dd_d + PyUFunc_ff_f + PyUFunc_dd_d + PyUFunc_gg_g + PyUFunc_FF_F_As_DD_D + PyUFunc_DD_D + PyUFunc_FF_F + PyUFunc_GG_G + PyUFunc_OO_O + PyUFunc_OO_O_method + PyUFunc_f_f_As_d_d + PyUFunc_d_d + PyUFunc_f_f + PyUFunc_g_g + PyUFunc_F_F_As_D_D + PyUFunc_F_F + PyUFunc_D_D + PyUFunc_G_G + PyUFunc_O_O + PyUFunc_O_O_method + PyUFunc_On_Om + + Where: + + f -- float + d -- double + g -- long double + F -- complex float + D -- complex double + G -- complex long double + O -- python object + + It is difficult to assure that each of these loops is entered from the + Python level as the special cased loops are a moving target and the + corresponding types are architecture dependent. We probably need to + define C level testing ufuncs to get at them. For the time being, I've + just looked at the signatures registered in the build directory to find + relevant functions. + + """ + np_dtypes = [ + (np.single, np.single), (np.single, np.double), + (np.csingle, np.csingle), (np.csingle, np.cdouble), + (np.double, np.double), (np.longdouble, np.longdouble), + (np.cdouble, np.cdouble), (np.clongdouble, np.clongdouble)] + + @pytest.mark.parametrize('input_dtype,output_dtype', np_dtypes) + def test_unary_PyUFunc(self, input_dtype, output_dtype, f=np.exp, x=0, y=1): + xs = np.full(10, input_dtype(x), dtype=output_dtype) + ys = f(xs)[::2] + assert_allclose(ys, y) + assert_equal(ys.dtype, output_dtype) + + def f2(x, y): + return x**y + + @pytest.mark.parametrize('input_dtype,output_dtype', np_dtypes) + def test_binary_PyUFunc(self, input_dtype, output_dtype, f=f2, x=0, y=1): + xs = np.full(10, input_dtype(x), dtype=output_dtype) + ys = f(xs, xs)[::2] + assert_allclose(ys, y) + assert_equal(ys.dtype, output_dtype) + + # class to use in testing object method loops + class foo(object): + def conjugate(self): + return np.bool_(1) + + def logical_xor(self, obj): + return np.bool_(1) + + def test_unary_PyUFunc_O_O(self): + x = np.ones(10, dtype=object) + assert_(np.all(np.abs(x) == 1)) + + def test_unary_PyUFunc_O_O_method(self, foo=foo): + x = np.full(10, foo(), dtype=object) + assert_(np.all(np.conjugate(x) == True)) + + def test_binary_PyUFunc_OO_O(self): + x = np.ones(10, dtype=object) + assert_(np.all(np.add(x, x) == 2)) + + def test_binary_PyUFunc_OO_O_method(self, foo=foo): + x = np.full(10, foo(), dtype=object) + assert_(np.all(np.logical_xor(x, x))) + + def test_binary_PyUFunc_On_Om_method(self, foo=foo): + x = np.full((10, 2, 3), foo(), dtype=object) + assert_(np.all(np.logical_xor(x, x))) + + class TestUfunc(object): def test_pickle(self): for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): @@ -65,146 +164,6 @@ class TestUfunc(object): idx = np.array(list(zip(np.arange(L - 2), np.arange(L - 2) + 2))).ravel() assert_array_equal(np.add.reduceat(x, idx)[::2], [1, 3, 5, 7]) - def test_generic_loops(self): - """Test generic loops. - - The loops to be tested are: - - PyUFunc_ff_f_As_dd_d - PyUFunc_ff_f - PyUFunc_dd_d - PyUFunc_gg_g - PyUFunc_FF_F_As_DD_D - PyUFunc_DD_D - PyUFunc_FF_F - PyUFunc_GG_G - PyUFunc_OO_O - PyUFunc_OO_O_method - PyUFunc_f_f_As_d_d - PyUFunc_d_d - PyUFunc_f_f - PyUFunc_g_g - PyUFunc_F_F_As_D_D - PyUFunc_F_F - PyUFunc_D_D - PyUFunc_G_G - PyUFunc_O_O - PyUFunc_O_O_method - PyUFunc_On_Om - - Where: - - f -- float - d -- double - g -- long double - F -- complex float - D -- complex double - G -- complex long double - O -- python object - - It is difficult to assure that each of these loops is entered from the - Python level as the special cased loops are a moving target and the - corresponding types are architecture dependent. We probably need to - define C level testing ufuncs to get at them. For the time being, I've - just looked at the signatures registered in the build directory to find - relevant functions. - - Fixme, currently untested: - - PyUFunc_ff_f_As_dd_d - PyUFunc_FF_F_As_DD_D - PyUFunc_f_f_As_d_d - PyUFunc_F_F_As_D_D - PyUFunc_On_Om - - """ - fone = np.exp - ftwo = lambda x, y: x**y - fone_val = 1 - ftwo_val = 1 - # check unary PyUFunc_f_f. - msg = "PyUFunc_f_f" - x = np.zeros(10, dtype=np.single)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - # check unary PyUFunc_d_d. - msg = "PyUFunc_d_d" - x = np.zeros(10, dtype=np.double)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - # check unary PyUFunc_g_g. - msg = "PyUFunc_g_g" - x = np.zeros(10, dtype=np.longdouble)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - # check unary PyUFunc_F_F. - msg = "PyUFunc_F_F" - x = np.zeros(10, dtype=np.csingle)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - # check unary PyUFunc_D_D. - msg = "PyUFunc_D_D" - x = np.zeros(10, dtype=np.cdouble)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - # check unary PyUFunc_G_G. - msg = "PyUFunc_G_G" - x = np.zeros(10, dtype=np.clongdouble)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - - # check binary PyUFunc_ff_f. - msg = "PyUFunc_ff_f" - x = np.ones(10, dtype=np.single)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - # check binary PyUFunc_dd_d. - msg = "PyUFunc_dd_d" - x = np.ones(10, dtype=np.double)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - # check binary PyUFunc_gg_g. - msg = "PyUFunc_gg_g" - x = np.ones(10, dtype=np.longdouble)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - # check binary PyUFunc_FF_F. - msg = "PyUFunc_FF_F" - x = np.ones(10, dtype=np.csingle)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - # check binary PyUFunc_DD_D. - msg = "PyUFunc_DD_D" - x = np.ones(10, dtype=np.cdouble)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - # check binary PyUFunc_GG_G. - msg = "PyUFunc_GG_G" - x = np.ones(10, dtype=np.clongdouble)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - - # class to use in testing object method loops - class foo(object): - def conjugate(self): - return np.bool_(1) - - def logical_xor(self, obj): - return np.bool_(1) - - # check unary PyUFunc_O_O - msg = "PyUFunc_O_O" - x = np.ones(10, dtype=object)[0::2] - assert_(np.all(np.abs(x) == 1), msg) - # check unary PyUFunc_O_O_method - msg = "PyUFunc_O_O_method" - x = np.zeros(10, dtype=object)[0::2] - for i in range(len(x)): - x[i] = foo() - assert_(np.all(np.conjugate(x) == True), msg) - - # check binary PyUFunc_OO_O - msg = "PyUFunc_OO_O" - x = np.ones(10, dtype=object)[0::2] - assert_(np.all(np.add(x, x) == 2), msg) - # check binary PyUFunc_OO_O_method - msg = "PyUFunc_OO_O_method" - x = np.zeros(10, dtype=object)[0::2] - for i in range(len(x)): - x[i] = foo() - assert_(np.all(np.logical_xor(x, x)), msg) - - # check PyUFunc_On_Om - # fixme -- I don't know how to do this yet - def test_all_ufunc(self): """Try to check presence and results of all ufuncs. @@ -379,45 +338,21 @@ class TestUfunc(object): assert_equal(flags, (self.can_ignore, self.size_inferred, 0)) assert_equal(sizes, (3, -1, 9)) - def test_signature_failure0(self): - # in the following calls, a ValueError should be raised because - # of error in core signature - # FIXME These should be using assert_raises + def test_signature_failure_extra_parenthesis(self): + with assert_raises(ValueError): + umt.test_signature(2, 1, "((i)),(i)->()") - # error: extra parenthesis - msg = "core_sig: extra parenthesis" - try: - ret = umt.test_signature(2, 1, "((i)),(i)->()") - assert_equal(ret, None, err_msg=msg) - except ValueError: - pass - - def test_signature_failure1(self): - # error: parenthesis matching - msg = "core_sig: parenthesis matching" - try: - ret = umt.test_signature(2, 1, "(i),)i(->()") - assert_equal(ret, None, err_msg=msg) - except ValueError: - pass + def test_signature_failure_mismatching_parenthesis(self): + with assert_raises(ValueError): + umt.test_signature(2, 1, "(i),)i(->()") - def test_signature_failure2(self): - # error: incomplete signature. letters outside of parenthesis are ignored - msg = "core_sig: incomplete signature" - try: - ret = umt.test_signature(2, 1, "(i),->()") - assert_equal(ret, None, err_msg=msg) - except ValueError: - pass + def test_signature_failure_signature_missing_input_arg(self): + with assert_raises(ValueError): + umt.test_signature(2, 1, "(i),->()") - def test_signature_failure3(self): - # error: incomplete signature. 2 output arguments are specified - msg = "core_sig: incomplete signature" - try: - ret = umt.test_signature(2, 2, "(i),(i)->()") - assert_equal(ret, None, err_msg=msg) - except ValueError: - pass + def test_signature_failure_signature_missing_output_arg(self): + with assert_raises(ValueError): + umt.test_signature(2, 2, "(i),(i)->()") def test_get_signature(self): assert_equal(umt.inner1d.signature, "(i),(i)->()") @@ -596,6 +531,12 @@ class TestUfunc(object): assert_equal(np.sum(np.ones((2, 3, 5), dtype=np.int64), axis=(0, 2), initial=2), [12, 12, 12]) + def test_sum_where(self): + # More extensive tests done in test_reduction_with_where. + assert_equal(np.sum([[1., 2.], [3., 4.]], where=[True, False]), 4.) + assert_equal(np.sum([[1., 2.], [3., 4.]], axis=0, initial=5., + where=[True, False]), [9., 5.]) + def test_inner1d(self): a = np.arange(6).reshape((2, 3)) assert_array_equal(umt.inner1d(a, a), np.sum(a*a, axis=-1)) @@ -623,6 +564,18 @@ class TestUfunc(object): b = np.arange(3).reshape((3, 1, 1)) assert_raises(ValueError, umt.inner1d, a, b) + # Writing to a broadcasted array with overlap should warn, gh-2705 + a = np.arange(2) + b = np.arange(4).reshape((2, 2)) + u, v = np.broadcast_arrays(a, b) + assert_equal(u.strides[0], 0) + x = u + v + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + u += v + assert_equal(len(w), 1) + assert_(x[0,0] != u[0, 0]) + def test_type_cast(self): msg = "type cast" a = np.arange(6, dtype='short').reshape((2, 3)) @@ -1162,6 +1115,8 @@ class TestUfunc(object): assert_equal(np.array([[1]], dtype=object).sum(), 1) assert_equal(np.array([[[1, 2]]], dtype=object).sum((0, 1)), [1, 2]) assert_equal(np.array([1], dtype=object).sum(initial=1), 2) + assert_equal(np.array([[1], [2, 3]], dtype=object) + .sum(initial=[0], where=[False, True]), [0, 2, 3]) def test_object_array_accumulate_inplace(self): # Checks that in-place accumulates work, see also gh-7402 @@ -1396,6 +1351,44 @@ class TestUfunc(object): res = np.add.reduce(a, initial=5) assert_equal(res, 15) + @pytest.mark.parametrize('axis', (0, 1, None)) + @pytest.mark.parametrize('where', (np.array([False, True, True]), + np.array([[True], [False], [True]]), + np.array([[True, False, False], + [False, True, False], + [False, True, True]]))) + def test_reduction_with_where(self, axis, where): + a = np.arange(9.).reshape(3, 3) + a_copy = a.copy() + a_check = np.zeros_like(a) + np.positive(a, out=a_check, where=where) + + res = np.add.reduce(a, axis=axis, where=where) + check = a_check.sum(axis) + assert_equal(res, check) + # Check we do not overwrite elements of a internally. + assert_array_equal(a, a_copy) + + @pytest.mark.parametrize(('axis', 'where'), + ((0, np.array([True, False, True])), + (1, [True, True, False]), + (None, True))) + @pytest.mark.parametrize('initial', (-np.inf, 5.)) + def test_reduction_with_where_and_initial(self, axis, where, initial): + a = np.arange(9.).reshape(3, 3) + a_copy = a.copy() + a_check = np.full(a.shape, -np.inf) + np.positive(a, out=a_check, where=where) + + res = np.maximum.reduce(a, axis=axis, where=where, initial=initial) + check = a_check.max(axis, initial=initial) + assert_equal(res, check) + + def test_reduction_where_initial_needed(self): + a = np.arange(9.).reshape(3, 3) + m = [False, True, False] + assert_raises(ValueError, np.maximum.reduce, a, where=m) + def test_identityless_reduction_nonreorderable(self): a = np.array([[8.0, 2.0, 2.0], [1.0, 0.5, 0.25]]) @@ -1532,6 +1525,7 @@ class TestUfunc(object): result = struct_ufunc.add_triplet(a, b) assert_equal(result, np.array([(2, 4, 6)], dtype='u8,u8,u8')) + assert_raises(RuntimeError, struct_ufunc.register_fail) def test_custom_ufunc(self): a = np.array( @@ -1749,16 +1743,19 @@ class TestUfunc(object): assert_equal(f(d, 0, None, None, True), r.reshape((1,) + r.shape)) assert_equal(f(d, 0, None, None, False, 0), r) assert_equal(f(d, 0, None, None, False, initial=0), r) + assert_equal(f(d, 0, None, None, False, 0, True), r) + assert_equal(f(d, 0, None, None, False, 0, where=True), r) # multiple keywords assert_equal(f(d, axis=0, dtype=None, out=None, keepdims=False), r) assert_equal(f(d, 0, dtype=None, out=None, keepdims=False), r) assert_equal(f(d, 0, None, out=None, keepdims=False), r) - assert_equal(f(d, 0, None, out=None, keepdims=False, initial=0), r) + assert_equal(f(d, 0, None, out=None, keepdims=False, initial=0, + where=True), r) # too little assert_raises(TypeError, f) # too much - assert_raises(TypeError, f, d, 0, None, None, False, 0, 1) + assert_raises(TypeError, f, d, 0, None, None, False, 0, True, 1) # invalid axis assert_raises(TypeError, f, d, "invalid") assert_raises(TypeError, f, d, axis="invalid") @@ -1857,3 +1854,83 @@ class TestUfunc(object): def test_no_doc_string(self): # gh-9337 assert_('\n' not in umt.inner1d_no_doc.__doc__) + + def test_invalid_args(self): + # gh-7961 + exc = pytest.raises(TypeError, np.sqrt, None) + # minimally check the exception text + assert exc.match('loop of ufunc does not support') + + @pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')]) + def test_nat_is_not_finite(self, nat): + try: + assert not np.isfinite(nat) + except TypeError: + pass # ok, just not implemented + + @pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')]) + def test_nat_is_nan(self, nat): + try: + assert np.isnan(nat) + except TypeError: + pass # ok, just not implemented + + @pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')]) + def test_nat_is_not_inf(self, nat): + try: + assert not np.isinf(nat) + except TypeError: + pass # ok, just not implemented + + +@pytest.mark.parametrize('ufunc', [getattr(np, x) for x in dir(np) + if isinstance(getattr(np, x), np.ufunc)]) +def test_ufunc_types(ufunc): + ''' + Check all ufuncs that the correct type is returned. Avoid + object and boolean types since many operations are not defined for + for them. + + Choose the shape so even dot and matmul will succeed + ''' + for typ in ufunc.types: + # types is a list of strings like ii->i + if 'O' in typ or '?' in typ: + continue + inp, out = typ.split('->') + args = [np.ones((3, 3), t) for t in inp] + with warnings.catch_warnings(record=True): + warnings.filterwarnings("always") + res = ufunc(*args) + if isinstance(res, tuple): + outs = tuple(out) + assert len(res) == len(outs) + for r, t in zip(res, outs): + assert r.dtype == np.dtype(t) + else: + assert res.dtype == np.dtype(out) + +@pytest.mark.parametrize('ufunc', [getattr(np, x) for x in dir(np) + if isinstance(getattr(np, x), np.ufunc)]) +def test_ufunc_noncontiguous(ufunc): + ''' + Check that contiguous and non-contiguous calls to ufuncs + have the same results for values in range(9) + ''' + for typ in ufunc.types: + # types is a list of strings like ii->i + if any(set('O?mM') & set(typ)): + # bool, object, datetime are too irregular for this simple test + continue + inp, out = typ.split('->') + args_c = [np.empty(6, t) for t in inp] + args_n = [np.empty(18, t)[::3] for t in inp] + for a in args_c: + a.flat = range(1,7) + for a in args_n: + a.flat = range(1,7) + with warnings.catch_warnings(record=True): + warnings.filterwarnings("always") + res_c = ufunc(*args_c) + res_n = ufunc(*args_n) + assert_equal(res_c, res_n) diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index 2f8edebc0..cd2034d9c 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -5,6 +5,7 @@ import warnings import fnmatch import itertools import pytest +from fractions import Fraction import numpy.core.umath as ncu from numpy.core import _umath_tests as ncu_tests @@ -12,11 +13,10 @@ import numpy as np from numpy.testing import ( assert_, assert_equal, assert_raises, assert_raises_regex, assert_array_equal, assert_almost_equal, assert_array_almost_equal, - assert_allclose, assert_no_warnings, suppress_warnings, + assert_array_max_ulp, assert_allclose, assert_no_warnings, suppress_warnings, _gen_alignment_data ) - def on_powerpc(): """ True if we are running on a Power PC platform.""" return platform.processor() == 'powerpc' or \ @@ -273,6 +273,12 @@ class TestDivision(object): y = np.floor_divide(x**2, x) assert_equal(y, [1.e+110, 0], err_msg=msg) + def test_floor_division_signed_zero(self): + # Check that the sign bit is correctly set when dividing positive and + # negative zero by one. + x = np.zeros(10) + assert_equal(np.signbit(x//1), 0) + assert_equal(np.signbit((-x)//1), 1) def floor_divide_and_remainder(x, y): return (np.floor_divide(x, y), np.remainder(x, y)) @@ -643,6 +649,59 @@ class TestExp(object): yf = np.array(y, dtype=dt)*log2_ assert_almost_equal(np.exp(yf), xf) +class TestSpecialFloats(object): + def test_exp_values(self): + x = [np.nan, np.nan, np.inf, 0.] + y = [np.nan, -np.nan, np.inf, -np.inf] + for dt in ['f', 'd', 'g']: + xf = np.array(x, dtype=dt) + yf = np.array(y, dtype=dt) + assert_equal(np.exp(yf), xf) + + with np.errstate(over='raise'): + assert_raises(FloatingPointError, np.exp, np.float32(100.)) + assert_raises(FloatingPointError, np.exp, np.float32(1E19)) + + def test_log_values(self): + with np.errstate(all='ignore'): + x = [np.nan, np.nan, np.inf, np.nan, -np.inf, np.nan] + y = [np.nan, -np.nan, np.inf, -np.inf, 0., -1.0] + for dt in ['f', 'd', 'g']: + xf = np.array(x, dtype=dt) + yf = np.array(y, dtype=dt) + assert_equal(np.log(yf), xf) + + with np.errstate(divide='raise'): + assert_raises(FloatingPointError, np.log, np.float32(0.)) + + with np.errstate(invalid='raise'): + assert_raises(FloatingPointError, np.log, np.float32(-np.inf)) + assert_raises(FloatingPointError, np.log, np.float32(-1.0)) + +class TestExpLogFloat32(object): + def test_exp_float32(self): + np.random.seed(42) + x_f32 = np.float32(np.random.uniform(low=0.0,high=88.1,size=1000000)) + x_f64 = np.float64(x_f32) + assert_array_max_ulp(np.exp(x_f32), np.float32(np.exp(x_f64)), maxulp=2.6) + + def test_log_float32(self): + np.random.seed(42) + x_f32 = np.float32(np.random.uniform(low=0.0,high=1000,size=1000000)) + x_f64 = np.float64(x_f32) + assert_array_max_ulp(np.log(x_f32), np.float32(np.log(x_f64)), maxulp=3.9) + + def test_strided_exp_log_float32(self): + np.random.seed(42) + strides = np.random.randint(low=-100, high=100, size=100) + sizes = np.random.randint(low=1, high=2000, size=100) + for ii in sizes: + x_f32 = np.float32(np.random.uniform(low=0.01,high=88.1,size=ii)) + exp_true = np.exp(x_f32) + log_true = np.log(x_f32) + for jj in strides: + assert_equal(np.exp(x_f32[::jj]), exp_true[::jj]) + assert_equal(np.log(x_f32[::jj]), log_true[::jj]) class TestLogAddExp(_FilterInvalids): def test_logaddexp_values(self): @@ -1894,7 +1953,8 @@ class TestSpecialMethods(object): # reduce, kwargs res = np.multiply.reduce(a, axis='axis0', dtype='dtype0', out='out0', - keepdims='keep0', initial='init0') + keepdims='keep0', initial='init0', + where='where0') assert_equal(res[0], a) assert_equal(res[1], np.multiply) assert_equal(res[2], 'reduce') @@ -1903,7 +1963,8 @@ class TestSpecialMethods(object): 'out': ('out0',), 'keepdims': 'keep0', 'axis': 'axis0', - 'initial': 'init0'}) + 'initial': 'init0', + 'where': 'where0'}) # reduce, output equal to None removed, but not other explicit ones, # even if they are at their default value. @@ -1913,14 +1974,18 @@ class TestSpecialMethods(object): assert_equal(res[4], {'axis': 0, 'keepdims': True}) res = np.multiply.reduce(a, None, out=(None,), dtype=None) assert_equal(res[4], {'axis': None, 'dtype': None}) - res = np.multiply.reduce(a, 0, None, None, False, 2) - assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, 'initial': 2}) - # np._NoValue ignored for initial. - res = np.multiply.reduce(a, 0, None, None, False, np._NoValue) - assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False}) - # None kept for initial. - res = np.multiply.reduce(a, 0, None, None, False, None) - assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, 'initial': None}) + res = np.multiply.reduce(a, 0, None, None, False, 2, True) + assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, + 'initial': 2, 'where': True}) + # np._NoValue ignored for initial + res = np.multiply.reduce(a, 0, None, None, False, + np._NoValue, True) + assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, + 'where': True}) + # None kept for initial, True for where. + res = np.multiply.reduce(a, 0, None, None, False, None, True) + assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, + 'initial': None, 'where': True}) # reduce, wrong args assert_raises(ValueError, np.multiply.reduce, a, out=()) @@ -2448,6 +2513,42 @@ class TestRationalFunctions(object): assert_equal(np.gcd(2**100, 3**100), 1) +class TestRoundingFunctions(object): + + def test_object_direct(self): + """ test direct implementation of these magic methods """ + class C: + def __floor__(self): + return 1 + def __ceil__(self): + return 2 + def __trunc__(self): + return 3 + + arr = np.array([C(), C()]) + assert_equal(np.floor(arr), [1, 1]) + assert_equal(np.ceil(arr), [2, 2]) + assert_equal(np.trunc(arr), [3, 3]) + + def test_object_indirect(self): + """ test implementations via __float__ """ + class C: + def __float__(self): + return -2.5 + + arr = np.array([C(), C()]) + assert_equal(np.floor(arr), [-3, -3]) + assert_equal(np.ceil(arr), [-2, -2]) + with pytest.raises(TypeError): + np.trunc(arr) # consistent with math.trunc + + def test_fraction(self): + f = Fraction(-4, 3) + assert_equal(np.floor(f), -2) + assert_equal(np.ceil(f), -1) + assert_equal(np.trunc(f), -1) + + class TestComplexFunctions(object): funcs = [np.arcsin, np.arccos, np.arctan, np.arcsinh, np.arccosh, np.arctanh, np.sin, np.cos, np.tan, np.exp, @@ -2912,3 +3013,14 @@ def test_signaling_nan_exceptions(): with assert_no_warnings(): a = np.ndarray(shape=(), dtype='float32', buffer=b'\x00\xe0\xbf\xff') np.isnan(a) + +@pytest.mark.parametrize("arr", [ + np.arange(2), + np.matrix([0, 1]), + np.matrix([[0, 1], [2, 5]]), + ]) +def test_outer_subclass_preserve(arr): + # for gh-8661 + class foo(np.ndarray): pass + actual = np.multiply.outer(arr.view(foo), arr.view(foo)) + assert actual.__class__.__name__ == 'foo' diff --git a/numpy/core/tests/test_umath_complex.py b/numpy/core/tests/test_umath_complex.py index 785ae8c57..1f5b4077f 100644 --- a/numpy/core/tests/test_umath_complex.py +++ b/numpy/core/tests/test_umath_complex.py @@ -5,7 +5,8 @@ import platform import pytest import numpy as np -import numpy.core.umath as ncu +# import the c-extension module directly since _arg is not exported via umath +import numpy.core._multiarray_umath as ncu from numpy.testing import ( assert_raises, assert_equal, assert_array_equal, assert_almost_equal ) |