From c05a7ee56bf9a54c357968d5d38e67cc1dabfa60 Mon Sep 17 00:00:00 2001 From: John Kirkham Date: Thu, 8 Dec 2016 14:55:32 -0500 Subject: [PATCH] Break out all measurement functions. --- kenjutsu/core.py | 107 +--------------------- kenjutsu/measure.py | 108 ++++++++++++++++++++++ tests/test_measure.py | 202 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 314 insertions(+), 103 deletions(-) diff --git a/kenjutsu/core.py b/kenjutsu/core.py index 022baff..6f0f119 100644 --- a/kenjutsu/core.py +++ b/kenjutsu/core.py @@ -26,118 +26,19 @@ import itertools -import numbers import operator -import math import warnings import kenjutsu.format +import kenjutsu.measure reformat_slice = kenjutsu.format.reformat_slice reformat_slices = kenjutsu.format.reformat_slices - -class UnknownSliceLengthException(Exception): - """ - Raised if a slice does not have a known length. - """ - - pass - - -def len_slice(a_slice, a_length=None): - """ - Determines how many elements a slice will contain. - - Raises: - UnknownSliceLengthException: Will raise an exception if - a_slice.stop and a_length is None. - - Args: - a_slice(slice): a slice to reformat. - - a_length(int): a length to fill for stopping if not - provided. - - Returns: - (slice): a new slice with as many values filled in as - possible. - - Examples: - - >>> len_slice(slice(2, None), 10) - 8 - - >>> len_slice(slice(2, 6)) - 4 - """ - - if isinstance(a_slice, numbers.Integral): - raise TypeError( - "An integral index does not provide an object with a length." - ) - - new_slice = reformat_slice(a_slice, a_length) - - new_slice_size = 0 - if isinstance(new_slice, slice): - if new_slice.stop is None: - if new_slice.step > 0: - raise UnknownSliceLengthException( - "Cannot determine slice length without a defined end" - " point. The reformatted slice was %s." % repr(new_slice) - ) - else: - new_slice = slice(new_slice.start, -1, new_slice.step) - - new_slice_diff = float(new_slice.stop - new_slice.start) - - new_slice_size = int(math.ceil(new_slice_diff / new_slice.step)) - else: - new_slice_size = len(new_slice) - - return(new_slice_size) - - -def len_slices(slices, lengths=None): - """ - Takes a tuple of slices and reformats them to fill in as many undefined - values as possible. - - Args: - slices(tuple(slice)): a tuple of slices to reformat. - lengths(tuple(int)): a tuple of lengths to fill. - - Returns: - (slice): a tuple of slices with all default - values filled if possible. - - Examples: - - >>> len_slices( - ... ( - ... slice(None), - ... slice(3, None), - ... slice(None, 5), - ... slice(None, None, 2) - ... ), - ... (10, 13, 15, 20) - ... ) - (10, 10, 5, 10) - """ - - new_slices = reformat_slices(slices, lengths) - - lens = [] - - for each_slice in new_slices: - if not isinstance(each_slice, numbers.Integral): - lens.append(len_slice(each_slice)) - - lens = tuple(lens) - - return(lens) +UnknownSliceLengthException = kenjutsu.measure.UnknownSliceLengthException +len_slice = kenjutsu.measure.len_slice +len_slices = kenjutsu.measure.len_slices def split_blocks(space_shape, block_shape, block_halo=None): diff --git a/kenjutsu/measure.py b/kenjutsu/measure.py index b856e7a..9155886 100644 --- a/kenjutsu/measure.py +++ b/kenjutsu/measure.py @@ -1,4 +1,112 @@ +from __future__ import absolute_import + __author__ = "John Kirkham " __date__ = "$Dec 08, 2016 14:49:29 GMT-0500$" +import numbers +import math + +import kenjutsu.format + + +class UnknownSliceLengthException(Exception): + """ + Raised if a slice does not have a known length. + """ + + pass + + +def len_slice(a_slice, a_length=None): + """ + Determines how many elements a slice will contain. + + Raises: + UnknownSliceLengthException: Will raise an exception if + a_slice.stop and a_length is None. + + Args: + a_slice(slice): a slice to reformat. + + a_length(int): a length to fill for stopping if not + provided. + + Returns: + (slice): a new slice with as many values filled in as + possible. + + Examples: + + >>> len_slice(slice(2, None), 10) + 8 + + >>> len_slice(slice(2, 6)) + 4 + """ + + if isinstance(a_slice, numbers.Integral): + raise TypeError( + "An integral index does not provide an object with a length." + ) + + new_slice = kenjutsu.format.reformat_slice(a_slice, a_length) + + new_slice_size = 0 + if isinstance(new_slice, slice): + if new_slice.stop is None: + if new_slice.step > 0: + raise UnknownSliceLengthException( + "Cannot determine slice length without a defined end" + " point. The reformatted slice was %s." % repr(new_slice) + ) + else: + new_slice = slice(new_slice.start, -1, new_slice.step) + + new_slice_diff = float(new_slice.stop - new_slice.start) + + new_slice_size = int(math.ceil(new_slice_diff / new_slice.step)) + else: + new_slice_size = len(new_slice) + + return(new_slice_size) + + +def len_slices(slices, lengths=None): + """ + Takes a tuple of slices and reformats them to fill in as many undefined + values as possible. + + Args: + slices(tuple(slice)): a tuple of slices to reformat. + lengths(tuple(int)): a tuple of lengths to fill. + + Returns: + (slice): a tuple of slices with all default + values filled if possible. + + Examples: + + >>> len_slices( + ... ( + ... slice(None), + ... slice(3, None), + ... slice(None, 5), + ... slice(None, None, 2) + ... ), + ... (10, 13, 15, 20) + ... ) + (10, 10, 5, 10) + """ + + new_slices = kenjutsu.format.reformat_slices(slices, lengths) + + lens = [] + + for each_slice in new_slices: + if not isinstance(each_slice, numbers.Integral): + lens.append(len_slice(each_slice)) + + lens = tuple(lens) + + return(lens) diff --git a/tests/test_measure.py b/tests/test_measure.py index b856e7a..079dfab 100644 --- a/tests/test_measure.py +++ b/tests/test_measure.py @@ -1,4 +1,206 @@ +#!/usr/bin/env python + +# -*- coding: utf-8 -*- + __author__ = "John Kirkham " __date__ = "$Dec 08, 2016 14:49:29 GMT-0500$" +import doctest +import itertools +import operator +import sys +import unittest + +from kenjutsu import measure + + +try: + irange = xrange +except NameError: + irange = range + + +# Load doctests from `measure`. +def load_tests(loader, tests, ignore): + tests.addTests(doctest.DocTestSuite(measure)) + return tests + + +class TestMeasure(unittest.TestCase): + def setUp(self): + pass + + + def test_len_slice(self): + with self.assertRaises(measure.UnknownSliceLengthException): + measure.len_slice(slice(None)) + + for size in [10, 11, 12]: + excess = size + 3 + each_range = range(size) + for start in itertools.chain([None], irange(-excess, excess)): + for stop in itertools.chain([None], irange(-excess, excess)): + for step in itertools.chain(irange(-excess, excess)): + step = None if step == 0 else step + + a_slice = slice(start, stop, step) + + l = measure.len_slice(a_slice, size) + self.assertEqual( + l, + len(each_range[a_slice]) + ) + + a_slice = list() + a_slice.append(0 if start is None else start) + a_slice.append(0 if stop is None else stop) + a_slice.append(0 if step is None else step) + + a_op = operator.itemgetter(*a_slice) + + expected_result = None + try: + expected_result = a_op(each_range) + except IndexError: + pass + + if expected_result is not None: + l = measure.len_slice(a_slice, size) + self.assertEqual(len(expected_result), l) + + if start is not None: + a_slice = start + + with self.assertRaises(TypeError): + measure.len_slice(a_slice, size) + + self.assertEqual( + measure.len_slice(Ellipsis, size), + len(each_range[:]) + ) + + self.assertEqual( + measure.len_slice(tuple(), size), + len(each_range[:]) + ) + + + def test_len_slices(self): + with self.assertRaises(measure.UnknownSliceLengthException): + measure.len_slices(( + slice(None), + slice(3, None), + slice(None, 5), + slice(None, None, 2) + )) + + l = measure.len_slices(Ellipsis, 10) + self.assertEqual( + l, + (10,) + ) + + l = measure.len_slices(tuple(), 10) + self.assertEqual( + l, + (10,) + ) + + l = measure.len_slices(slice(None), 10) + self.assertEqual( + l, + (10,) + ) + + l = measure.len_slices((slice(None),), 10) + self.assertEqual( + l, + (10,) + ) + + l = measure.len_slices( + ( + -1, + slice(None), + slice(3, None), + slice(None, 5), + slice(None, None, 2), + [-1, -2, -1, 1, 5] + ), + (12, 10, 13, 15, 20, 10) + ) + self.assertEqual( + l, + (10, 10, 5, 10, 5) + ) + + l = measure.len_slices( + Ellipsis, + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (2, 3, 4, 5) + ) + + l = measure.len_slices( + ( + Ellipsis, + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (2, 3, 4, 1) + ) + + l = measure.len_slices( + ( + slice(0, 1), + Ellipsis + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (1, 3, 4, 5) + ) + + l = measure.len_slices( + ( + slice(0, 1), + Ellipsis, + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (1, 3, 4, 1) + ) + + l = measure.len_slices( + ( + slice(0, 1), + Ellipsis, + slice(0, 1), + slice(0, 1), + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (1, 1, 1, 1) + ) + + + def tearDown(self): + pass + + + +if __name__ == '__main__': + sys.exit(unittest.main())