diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/tests/test_deprecations.py | 295 | ||||
-rw-r--r-- | numpy/core/tests/test_indexing.py | 5 |
2 files changed, 192 insertions, 108 deletions
diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 9b77a1fc1..8948291a3 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -6,65 +6,127 @@ to document how deprecations should eventually be turned into errors. from __future__ import division, absolute_import, print_function import sys + import warnings from nose.plugins.skip import SkipTest import numpy as np from numpy.testing import dec, run_module_suite, assert_raises +import operator -def assert_deprecated(f, *args, **kwargs): - """Check if DeprecationWarning raised as error. - - The warning environment is assumed to have been set up so that the - appropriate DeprecationWarning has been turned into an error. We do not - use assert_warns here as the desire is to check that an error will be - raised if the deprecation is changed to an error and there may be other - errors that would override a warning. It is a fine point as to which - error should appear first. - - Parameters - ---------- - f : callable - A function that will exhibit the deprecation. It need not be - deprecated itself, but can be used to execute deprecated code. - - """ - assert_raises(DeprecationWarning, f, *args, **kwargs) - - -def assert_not_deprecated(f, *args, **kwargs): - """Check that DeprecationWarning not raised as error. +class _DeprecationTestCase(object): + # Just as warning: warnings uses re.match, so the start of this message + # must match. + message = '' - The warning environment is assumed to have been set up so that the - appropriate DeprecationWarning has been turned into an error. This - function checks that no warning is raised when `f` is executed. - - Parameters - ---------- - f : callable - A function that will exhibit no deprecation. It can be used to - execute code that should not raise DeprecationWarning. + def setUp(self): + self.warn_ctx = warnings.catch_warnings(record=True) + self.log = self.warn_ctx.__enter__() - """ - try: - f(*args, **kwargs) - except DeprecationWarning: - raise AssertionError() + # Do *not* ignore other DeprecationWarnings. Ignoring warnings + # can give very confusing results because of + # http://bugs.python.org/issue4180 and it is probably simplest to + # try to keep the tests cleanly giving only the right warning type. + # (While checking them set to "error" those are ignored anyway) + # We still have them show up, because otherwise they would be raised + warnings.filterwarnings("always", category=DeprecationWarning) + warnings.filterwarnings("always", message=self.message, + category=DeprecationWarning) -class TestFloatScalarIndexDeprecation(object): + def tearDown(self): + self.warn_ctx.__exit__() + + + def assert_deprecated(self, function, num=1, ignore_others=False, + function_fails=False, + exceptions=(DeprecationWarning,), args=(), kwargs={}): + """Test if DeprecationWarnings are given and raised. + + This first checks if the function when called gives `num` + DeprecationWarnings, after that it tries to raise these + DeprecationWarnings and compares them with `exceptions`. + The exceptions can be different for cases where this code path + is simply not anticipated and the exception is replaced. + + Parameters + ---------- + f : callable + The function to test + num : int + Number of DeprecationWarnings to expect. This should normally be 1. + ignore_other : bool + Whether warnings of the wrong type should be ignored (note that + the message is not checked) + function_fails : bool + If the function would normally fail, setting this will check for + warnings inside a try/except block. + exceptions : Exception or tuple of Exceptions + Exception to expect when turning the warnings into an error. + The default checks for DeprecationWarnings. If exceptions is + empty the function is expected to run successfull. + args : tuple + Arguments for `f` + kwargs : dict + Keyword arguments for `f` + """ + # reset the log + self.log[:] = [] + + try: + function(*args, **kwargs) + except (Exception if function_fails else tuple()): + pass + # just in case, clear the registry + num_found = 0 + for warning in self.log: + if warning.category is DeprecationWarning: + num_found += 1 + elif not ignore_others: + raise AssertionError("expected DeprecationWarning but %s given" + % warning.category) + if num_found != num: + raise AssertionError("%i warnings found but %i expected" + % (len(self.log), num)) + + warnings.filterwarnings("error", message=self.message, + category=DeprecationWarning) + + try: + function(*args, **kwargs) + if exceptions != tuple(): + raise AssertionError("No error raised during function call") + except exceptions: + if exceptions == tuple(): + raise AssertionError("Error raised during function call") + finally: + warnings.filters.pop(0) + + + def assert_not_deprecated(self, function, args=(), kwargs={}): + """Test if DeprecationWarnings are given and raised. + + This is just a shorthand for: + + self.assert_deprecated(function, num=0, ignore_others=True, + exceptions=tuple(), args=args, kwargs=kwargs) + """ + self.assert_deprecated(function, num=0, ignore_others=True, + exceptions=tuple(), args=args, kwargs=kwargs) + + +class TestFloatNonIntegerArgumentDeprecation(_DeprecationTestCase): """ - These test that ``DeprecationWarning`` gets raised when you try to use - scalar indices that are not integers e.g. ``a[0.0]``, ``a[1.5, 0]``. - - grep "non-integer scalar" numpy/core/src/multiarray/* for all the calls - to ``DEPRECATE()``, except the one inside ``_validate_slice_parameter`` - which handles slicing (but see also - `TestFloatSliceParameterDeprecation`). + These test that ``DeprecationWarning`` is given when you try to use + non-integers as arguments to for indexing and slicing e.g. ``a[0.0:5]`` + and ``a[0.5]``, or other functions like ``array.reshape(1., -1)``. - When 2.4 support is dropped ``PyIndex_Check_Or_Unsupported`` should be - removed from ``npy_pycompat.h`` and changed to just ``PyIndex_Check``. + After deprecation, changes need to be done inside conversion_utils.c + in PyArray_PyIntAsIntp and possibly PyArray_IntpConverter. + In iterators.c the function slice_GetIndices could be removed in favor + of its python equivalent and in mapping.c the function _tuple_of_integers + can be simplified (iff ``np.array([1]).__index__()`` is also deprecated). As for the deprecation time-frame: via Ralf Gommers, @@ -72,31 +134,26 @@ class TestFloatScalarIndexDeprecation(object): version after 1.8 will be 6 months or 2 years after. I'd say 2 years is reasonable." - I interpret this to mean 2 years after the 1.8 release. + I interpret this to mean 2 years after the 1.8 release. Possibly + giving a PendingDeprecationWarning before that (which is visible + by default) """ + message = "using a non-integer number instead of an integer " \ + "will result in an error in the future" - def setUp(self): - warnings.filterwarnings("error", message="non-integer scalar index", - category=DeprecationWarning) - - - def tearDown(self): - warnings.filterwarnings("default", message="non-integer scalar index", - category=DeprecationWarning) - - - def test_deprecations(self): + def test_indexing(self): a = np.array([[[5]]]) + def assert_deprecated(*args, **kwargs): + self.assert_deprecated(*args, exceptions=(IndexError,), **kwargs) assert_deprecated(lambda: a[0.0]) - assert_deprecated(lambda: a[0.0]) assert_deprecated(lambda: a[0, 0.0]) assert_deprecated(lambda: a[0.0, 0]) assert_deprecated(lambda: a[0.0, :]) assert_deprecated(lambda: a[:, 0.0]) assert_deprecated(lambda: a[:, 0.0, :]) - assert_deprecated(lambda: a[0.0, :, :]) + assert_deprecated(lambda: a[0.0, :, :], num=2) # [1] assert_deprecated(lambda: a[0, 0, 0.0]) assert_deprecated(lambda: a[0.0, 0, 0]) assert_deprecated(lambda: a[0, 0.0, 0]) @@ -106,18 +163,21 @@ class TestFloatScalarIndexDeprecation(object): assert_deprecated(lambda: a[-1.4, :]) assert_deprecated(lambda: a[:, -1.4]) assert_deprecated(lambda: a[:, -1.4, :]) - assert_deprecated(lambda: a[-1.4, :, :]) + assert_deprecated(lambda: a[-1.4, :, :], num=2) # [1] assert_deprecated(lambda: a[0, 0, -1.4]) assert_deprecated(lambda: a[-1.4, 0, 0]) assert_deprecated(lambda: a[0, -1.4, 0]) + # [1] These are duplicate because of the _tuple_of_integers quick check + # Test that the slice parameter deprecation warning doesn't mask # the scalar index warning. - assert_deprecated(lambda: a[0.0:, 0.0]) - assert_deprecated(lambda: a[0.0:, 0.0, :]) + assert_deprecated(lambda: a[0.0:, 0.0], num=2) + assert_deprecated(lambda: a[0.0:, 0.0, :], num=2) - def test_valid_not_deprecated(self): + def test_valid_indexing(self): a = np.array([[[5]]]) + assert_not_deprecated = self.assert_not_deprecated assert_not_deprecated(lambda: a[np.array([0])]) assert_not_deprecated(lambda: a[[0, 0]]) @@ -126,40 +186,10 @@ class TestFloatScalarIndexDeprecation(object): assert_not_deprecated(lambda: a[:, :, :]) -class TestFloatSliceParameterDeprecation(object): - """ - These test that ``DeprecationWarning`` gets raised when you try to use - non-integers for slicing, e.g. ``a[0.0:5]``, ``a[::1.5]``, etc. - - When this is changed to an error, ``slice_GetIndices`` and - ``_validate_slice_parameter`` should probably be removed. Calls to - ``slice_GetIndices`` should be replaced by the standard Python API call - ``PySlice_GetIndicesEx``, since ``slice_GetIndices`` implements the - same thing but with int coercion and Python < 2.3 backwards - compatibility (which we have long since dropped as of this writing). - - As for the deprecation time-frame: via Ralf Gommers, - - "Hard to put that as a version number, since we don't know if the - version after 1.8 will be 6 months or 2 years after. I'd say 2 years is - reasonable." - - I interpret this to mean 2 years after the 1.8 release. - - """ - - def setUp(self): - warnings.filterwarnings("error", message="non-integer slice param", - category=DeprecationWarning) - - - def tearDown(self): - warnings.filterwarnings("default", message="non-integer slice param", - category=DeprecationWarning) - - - def test_deprecations(self): + def test_slicing(self): a = np.array([[5]]) + def assert_deprecated(*args, **kwargs): + self.assert_deprecated(*args, exceptions=(IndexError,), **kwargs) # start as float. assert_deprecated(lambda: a[0.0:]) @@ -180,18 +210,19 @@ class TestFloatSliceParameterDeprecation(object): assert_deprecated(lambda: a[::5.0, :]) assert_deprecated(lambda: a[:, 0:4:2.0]) # mixed. - assert_deprecated(lambda: a[1.0:2:2.0]) - assert_deprecated(lambda: a[1.0::2.0]) - assert_deprecated(lambda: a[0:, :2.0:2.0]) - assert_deprecated(lambda: a[1.0:1:4.0, :0]) - assert_deprecated(lambda: a[1.0:5.0:5.0, :]) - assert_deprecated(lambda: a[:, 0.4:4.0:2.0]) + assert_deprecated(lambda: a[1.0:2:2.0], num=2) + assert_deprecated(lambda: a[1.0::2.0], num=2) + assert_deprecated(lambda: a[0:, :2.0:2.0], num=2) + assert_deprecated(lambda: a[1.0:1:4.0, :0], num=2) + assert_deprecated(lambda: a[1.0:5.0:5.0, :], num=3) + assert_deprecated(lambda: a[:, 0.4:4.0:2.0], num=3) # should still get the DeprecationWarning if step = 0. - assert_deprecated(lambda: a[::0.0]) + assert_deprecated(lambda: a[::0.0], function_fails=True) - def test_valid_not_deprecated(self): + def test_valid_slicing(self): a = np.array([[[5]]]) + assert_not_deprecated = self.assert_not_deprecated assert_not_deprecated(lambda: a[::]) assert_not_deprecated(lambda: a[0:]) @@ -203,5 +234,57 @@ class TestFloatSliceParameterDeprecation(object): assert_not_deprecated(lambda: a[1:2:2]) + def test_non_integer_argument_deprecations(self): + a = np.array([[5]]) + + self.assert_deprecated(np.reshape, args=(a, (1., 1., -1)), num=2) + self.assert_deprecated(np.reshape, args=(a, (np.array(1.), -1))) + self.assert_deprecated(np.take, args=(a, [0], 1.)) + self.assert_deprecated(np.take, args=(a, [0], np.float64(1.))) + + +class TestBooleanArgumentDeprecation(_DeprecationTestCase): + """This tests that using a boolean as integer argument/indexing is + deprecated. + + This should be kept in sync with TestFloatNonIntegerArgumentDeprecation + and like it is handled in PyArray_PyIntAsIntp. + """ + message = "using a boolean instead of an integer " \ + "will result in an error in the future" + + def test_bool_as_int_argument(self): + a = np.array([[[1]]]) + + self.assert_deprecated(np.reshape, args=(a, (True, -1))) + self.assert_deprecated(np.take, args=(a, [0], False)) + self.assert_deprecated(lambda: a[False:True:True], exceptions=IndexError, num=3) + self.assert_deprecated(lambda: a[False,0], exceptions=IndexError) + self.assert_deprecated(lambda: a[False,0,0], exceptions=IndexError) + + +class TestArrayToIndexDeprecation(_DeprecationTestCase): + """This tests that creating an an index from an array is deprecated + if the array is not 0d. + + This can probably be deprecated somewhat faster then the integer + deprecations. The deprecation period started with NumPy 1.8. + For deprecation this needs changing of array_index in number.c + """ + message = "converting an array with ndim \> 0 to an index will result " \ + "in an error in the future" + + def test_array_to_index_deprecation(self): + # This drops into the non-integer deprecation, which is ignored here, + # so no exception is expected. The raising is effectively tested above. + a = np.array([[[1]]]) + + self.assert_deprecated(operator.index, args=(np.array([1]),)) + self.assert_deprecated(np.reshape, args=(a, (a, -1)), exceptions=()) + self.assert_deprecated(np.take, args=(a, [0], a), exceptions=()) + # Check slicing. Normal indexing checks arrays specifically. + self.assert_deprecated(lambda: a[a:a:a], exceptions=(), num=3) + + if __name__ == "__main__": - run_module_suite() + run_module_suite()
\ No newline at end of file diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py index 0a81a70d0..6d498b81f 100644 --- a/numpy/core/tests/test_indexing.py +++ b/numpy/core/tests/test_indexing.py @@ -73,8 +73,9 @@ class TestIndexing(TestCase): [7, 8, 9]]) # Python boolean converts to integer - assert_equal(a[True], a[1]) - assert_equal(a[False], a[0]) + # These are being deprecated (and test in test_deprecations) + #assert_equal(a[True], a[1]) + #assert_equal(a[False], a[0]) # Same with NumPy boolean scalar assert_equal(a[np.array(True)], a[1]) |