From 606cfd1cc940f5f6d69d9582aafade06aa44b703 Mon Sep 17 00:00:00 2001 From: Brad Walker Date: Sat, 13 Dec 2014 23:57:46 -0700 Subject: [PATCH 1/4] newrange: added attr read-only properties --- src/future/types/newrange.py | 12 ++++++++++++ tests/test_future/test_range.py | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/future/types/newrange.py b/src/future/types/newrange.py index b78c5700..4a61b54f 100644 --- a/src/future/types/newrange.py +++ b/src/future/types/newrange.py @@ -55,6 +55,18 @@ def __init__(self, *args): self._step = step self._len = (stop - start) // step + bool((stop - start) % step) + @property + def start(self): + return self._start + + @property + def stop(self): + return self._stop + + @property + def step(self): + return self._step + def __repr__(self): if self._step == 1: return 'range(%d, %d)' % (self._start, self._stop) diff --git a/tests/test_future/test_range.py b/tests/test_future/test_range.py index 0f158dc8..805aa88b 100644 --- a/tests/test_future/test_range.py +++ b/tests/test_future/test_range.py @@ -99,6 +99,16 @@ def test_slice_zero_step(self): with self.assertRaisesRegexp(ValueError, msg): range(8)[::0] + def test_properties(self): + # Exception string differs between PY2/3 + r = range(0) + with self.assertRaises(AttributeError): + r.start = 0 + with self.assertRaises(AttributeError): + r.stop = 0 + with self.assertRaises(AttributeError): + r.step = 0 + if __name__ == '__main__': unittest.main() From d0e7e4342c029320cdc3770f3c81a0314006e401 Mon Sep 17 00:00:00 2001 From: Brad Walker Date: Sat, 13 Dec 2014 23:35:11 -0700 Subject: [PATCH 2/4] simplified range_iterator (renamed for consistency) --- src/future/types/newrange.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/future/types/newrange.py b/src/future/types/newrange.py index 4a61b54f..09f7161e 100644 --- a/src/future/types/newrange.py +++ b/src/future/types/newrange.py @@ -19,6 +19,7 @@ """ from collections import Sequence, Iterator +from itertools import islice class newrange(Sequence): @@ -136,35 +137,27 @@ def __getitem_slice(self, slce): def __iter__(self): """Return an iterator which enumerates the elements of the sequence this range represents.""" - return rangeiterator(self) + return range_iterator(self) -class rangeiterator(Iterator): +class range_iterator(Iterator): """An iterator for a :class:`range`. """ - - def __init__(self, rangeobj): - self._range = rangeobj - - # Intialize the "last outputted value" to the value - # just before the first value; this simplifies next() - self._last = self._range._start - self._range._step - self._count = 0 + def __init__(self, range_): + self._stepper = islice(_count(range_.start, range_.step), len(range_)) def __iter__(self): - """An iterator is already an iterator, so return ``self``. - """ return self def next(self): - """Return the next element in the sequence represented - by the range we are iterating, or raise StopIteration - if we have passed the end of the sequence.""" - self._last += self._range._step - self._count += 1 - if self._count > self._range._len: - raise StopIteration() - return self._last + return next(self._stepper) + + +# itertools.count in Py 2.6 doesn't accept a step parameter +def _count(start=0, step=1): + while True: + yield start + start += step __all__ = ['newrange'] From e84bab2833ddfd89261c2d3eeba033e957c6f2b8 Mon Sep 17 00:00:00 2001 From: Brad Walker Date: Sat, 13 Dec 2014 23:50:04 -0700 Subject: [PATCH 3/4] newrange: fixed slices on empty ranges --- src/future/types/newrange.py | 4 +++- tests/test_future/test_range.py | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/future/types/newrange.py b/src/future/types/newrange.py index 09f7161e..a2ebeec0 100644 --- a/src/future/types/newrange.py +++ b/src/future/types/newrange.py @@ -132,7 +132,9 @@ def __getitem_slice(self, slce): of the sequence represented by this range. """ start, stop, step = slce.indices(self._len) - return newrange(self[start], stop + self._start, step * self._step) + return newrange(self._start + self._step*start, + self._start + stop, + self._step * step) def __iter__(self): """Return an iterator which enumerates the elements of the diff --git a/tests/test_future/test_range.py b/tests/test_future/test_range.py index 805aa88b..bc87b155 100644 --- a/tests/test_future/test_range.py +++ b/tests/test_future/test_range.py @@ -19,11 +19,15 @@ def test_bool_range(self): self.assertFalse(range(1, 1)) self.assertFalse(range(5, 2)) - def test_equality(self): + def test_equality_range(self): self.assertEqual(range(7), range(7)) self.assertEqual(range(0), range(1, 1)) self.assertEqual(range(0, 10, 3), range(0, 11, 3)) + def test_slice_empty_range(self): + self.assertEqual(range(0)[:], range(0)) + self.assertEqual(range(0)[::-1], range(-1, -1, -1)) + def test_slice_range(self): r = range(8) self.assertEqual(r[:], range(8)) From 2b096827217f5e23f8cbbfe5ca40fc14afc63e9f Mon Sep 17 00:00:00 2001 From: Brad Walker Date: Sat, 13 Dec 2014 23:51:17 -0700 Subject: [PATCH 4/4] simplified newrange.__reversed__ and corrected return type --- src/future/types/newrange.py | 7 +------ tests/test_future/test_range.py | 3 ++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/future/types/newrange.py b/src/future/types/newrange.py index a2ebeec0..432f11a1 100644 --- a/src/future/types/newrange.py +++ b/src/future/types/newrange.py @@ -107,12 +107,7 @@ def __contains__(self, value): return False def __reversed__(self): - """Return a range which represents a sequence whose - contents are the same as the sequence this range - represents, but in the opposite order.""" - sign = self._step / abs(self._step) - last = self._start + ((self._len - 1) * self._step) - return newrange(last, self._start - sign, -1 * self._step) + return iter(self[::-1]) def __getitem__(self, index): """Return the element at position ``index`` in the sequence diff --git a/tests/test_future/test_range.py b/tests/test_future/test_range.py index bc87b155..90f72712 100644 --- a/tests/test_future/test_range.py +++ b/tests/test_future/test_range.py @@ -6,12 +6,13 @@ from future.builtins import range from future.tests.base import unittest -from collections import Sequence +from collections import Iterator, Sequence class RangeTests(unittest.TestCase): def test_range(self): self.assertTrue(isinstance(range(0), Sequence)) + self.assertTrue(isinstance(reversed(range(0)), Iterator)) def test_bool_range(self): self.assertFalse(range(0))