from __future__ import print_function, division, absolute_import from functools import partial import itertools from itertools import chain, product, starmap import sys import numpy as np from numba import unittest_support as unittest from numba import jit, typeof, utils, TypingError from .support import TestCase, MemoryLeakMixin def slice_passing(sl): return sl.start, sl.stop, sl.step def slice_constructor(*args): sl = slice(*args) return sl.start, sl.stop, sl.step def slice_construct_and_use(args, l): sl = slice(*args) return l[sl] def slice_indices(s, *indargs): return s.indices(*indargs) class TestSlices(MemoryLeakMixin, TestCase): def test_slice_passing(self): """ Check passing a slice object to a Numba function. """ # NOTE this also checks slice attributes def check(a, b, c, d, e, f): sl = slice(a, b, c) got = cfunc(sl) self.assertPreciseEqual(got, (d, e, f)) maxposint = sys.maxsize maxnegint = -maxposint - 1 cfunc = jit(nopython=True)(slice_passing) # Positive steps start_cases = [(None, 0), (42, 42), (-1, -1)] stop_cases = [(None, maxposint), (9, 9), (-11, -11)] step_cases = [(None, 1), (12, 12)] for (a, d), (b, e), (c, f) in itertools.product(start_cases, stop_cases, step_cases): check(a, b, c, d, e, f) # Negative steps start_cases = [(None, maxposint), (42, 42), (-1, -1)] stop_cases = [(None, maxnegint), (9, 9), (-11, -11)] step_cases = [(-1, -1), (-12, -12)] for (a, d), (b, e), (c, f) in itertools.product(start_cases, stop_cases, step_cases): check(a, b, c, d, e, f) # Some member is neither integer nor None with self.assertRaises(TypeError): cfunc(slice(1.5, 1, 1)) def test_slice_constructor(self): """ Test the 'happy path' for slice() constructor in nopython mode. """ maxposint = sys.maxsize maxnegint = -maxposint - 1 a = np.arange(10) cfunc = jit(nopython=True)(slice_constructor) cfunc_use = jit(nopython=True)(slice_construct_and_use) for args, expected in [ ((None,), (0, maxposint, 1)), ((5,), (0, 5, 1)), ((None, None), (0, maxposint, 1)), ((1, None), (1, maxposint, 1)), ((None, 2), (0, 2, 1)), ((1, 2), (1, 2, 1)), ((None, None, 3), (0, maxposint, 3)), ((None, 2, 3), (0, 2, 3)), ((1, None, 3), (1, maxposint, 3)), ((1, 2, 3), (1, 2, 3)), ((None, None, -1), (maxposint, maxnegint, -1)), ((10, None, -1), (10, maxnegint, -1)), ((None, 5, -1), (maxposint, 5, -1)), ((10, 5, -1), (10, 5, -1)), ]: got = cfunc(*args) self.assertPreciseEqual(got, expected) usage = slice_construct_and_use(args, a) cusage = cfunc_use(args, a) self.assertPreciseEqual(usage, cusage) def test_slice_constructor_cases(self): """ Test that slice constructor behaves same in python and compiled code. """ options = (None, -1, 0, 1) arg_cases = chain.from_iterable( product(options, repeat=n) for n in range(5) ) array = np.arange(10) cfunc = jit(nopython=True)(slice_construct_and_use) self.disable_leak_check() for args in arg_cases: try: expected = slice_construct_and_use(args, array) except TypeError as py_type_e: # Catch cases of 0, or more than 3 arguments. # This becomes a typing error in numba n_args = len(args) self.assertRegexpMatches( str(py_type_e), r"slice expected at (most|least) (3|1) arguments?, got {}" .format(n_args) ) with self.assertRaises(TypingError) as numba_e: cfunc(args, array) self.assertIn( "Invalid use of Function", str(numba_e.exception) ) self.assertIn( ", ".join(str(typeof(arg)) for arg in args), str(numba_e.exception) ) except Exception as py_e: with self.assertRaises(type(py_e)) as numba_e: cfunc(args, array) self.assertIn( str(py_e), str(numba_e.exception) ) else: self.assertPreciseEqual(expected, cfunc(args, array)) def test_slice_indices(self): """Test that a numba slice returns same result for .indices as a python one.""" slices = starmap( slice, product( chain(range(-5, 5), (None,)), chain(range(-5, 5), (None,)), chain(range(-5, 5), (None,)) ) ) lengths = range(-2, 3) cfunc = jit(nopython=True)(slice_indices) for s, l in product(slices, lengths): if l < 0 and not utils.IS_PY3: # Passing a negative length to slice.indices in python2 is # undefined. See https://bugs.python.org/issue14794#msg174678 continue try: expected = slice_indices(s, l) except Exception as py_e: with self.assertRaises(type(py_e)) as numba_e: cfunc(s, l) self.assertIn( str(py_e), str(numba_e.exception) ) else: self.assertPreciseEqual(expected, cfunc(s, l)) def test_slice_indices_examples(self): """Tests for specific error cases.""" cslice_indices = jit(nopython=True)(slice_indices) with self.assertRaises(TypingError) as e: cslice_indices(slice(None), 1, 2, 3) self.assertIn( "indices() takes exactly one argument (3 given)", str(e.exception) ) with self.assertRaises(TypingError) as e: cslice_indices(slice(None, None, 0), 1.2) self.assertIn( "'%s' object cannot be interpreted as an integer" % typeof(1.2), str(e.exception) ) if __name__ == '__main__': unittest.main()