Skip to content

Commit 82b1025

Browse files
committed
Merge pull request #129 from bradmwalker/range-fixes
newrange equality and slicing fixed
2 parents 8f22812 + 8fe268b commit 82b1025

File tree

2 files changed

+111
-25
lines changed

2 files changed

+111
-25
lines changed

src/future/types/newrange.py

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Nearly identical to xrange.py, by Dan Crosta, from
2+
Nearly identical to xrange.py, by Dan Crosta, from
33
44
https://github.com/dcrosta/xrange.git
55
@@ -18,11 +18,8 @@
1818
https://late.am/post/2012/06/18/what-the-heck-is-an-xrange
1919
"""
2020

21-
from math import ceil
2221
from collections import Sequence, Iterator
2322

24-
from future.utils import PY3
25-
2623

2724
class newrange(Sequence):
2825
"""
@@ -64,10 +61,10 @@ def __repr__(self):
6461
return 'range(%d, %d, %d)' % (self._start, self._stop, self._step)
6562

6663
def __eq__(self, other):
67-
return isinstance(other, newrange) and \
68-
self._start == other._start and \
69-
self._stop == other._stop and \
70-
self._step == other._step
64+
return (isinstance(other, newrange) and
65+
(self._len == 0 == other._len or
66+
(self._start, self._step, self._len) ==
67+
(other._start, other._step, self._len)))
7168

7269
def __len__(self):
7370
return self._len
@@ -121,23 +118,8 @@ def __getitem_slice(self, slce):
121118
"""Return a range which represents the requested slce
122119
of the sequence represented by this range.
123120
"""
124-
start, stop, step = slce.start, slce.stop, slce.step
125-
if step == 0:
126-
raise ValueError('slice step cannot be 0')
127-
128-
start = start or self._start
129-
stop = stop or self._stop
130-
if start < 0:
131-
start = max(0, start + self._len)
132-
if stop < 0:
133-
stop = max(start, stop + self._len)
134-
135-
if step is None or step > 0:
136-
return newrange(start, stop, step or 1)
137-
else:
138-
rv = reversed(self)
139-
rv._step = step
140-
return rv
121+
start, stop, step = slce.indices(self._len)
122+
return newrange(self[start], stop + self._start, step * self._step)
141123

142124
def __iter__(self):
143125
"""Return an iterator which enumerates the elements of the

tests/test_future/test_range.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Tests for the backported class:`range` class.
4+
"""
5+
6+
from future.builtins import range
7+
from future.tests.base import unittest
8+
9+
from collections import Sequence
10+
11+
12+
class RangeTests(unittest.TestCase):
13+
def test_range(self):
14+
self.assertTrue(isinstance(range(0), Sequence))
15+
16+
def test_bool_range(self):
17+
self.assertFalse(range(0))
18+
self.assertTrue(range(1))
19+
self.assertFalse(range(1, 1))
20+
self.assertFalse(range(5, 2))
21+
22+
def test_equality(self):
23+
self.assertEqual(range(7), range(7))
24+
self.assertEqual(range(0), range(1, 1))
25+
self.assertEqual(range(0, 10, 3), range(0, 11, 3))
26+
27+
def test_slice_range(self):
28+
r = range(8)
29+
self.assertEqual(r[:], range(8))
30+
self.assertEqual(r[:2], range(2))
31+
self.assertEqual(r[:-2], range(6))
32+
self.assertEqual(r[2:], range(2, 8))
33+
self.assertEqual(r[-2:], range(6, 8))
34+
self.assertEqual(r[2:-2], range(2, 6))
35+
self.assertEqual(r[-2:2:-1], range(6, 2, -1))
36+
r = r[::-1]
37+
self.assertEqual(r, range(7, -1, -1))
38+
self.assertEqual(r[:], range(7, -1, -1))
39+
self.assertEqual(r[:2], range(7, 5, -1))
40+
self.assertEqual(r[:-2], range(7, 1, -1))
41+
self.assertEqual(r[2:], range(5, -1, -1))
42+
self.assertEqual(r[-2:], range(1, -1, -1))
43+
self.assertEqual(r[2:-2], range(5, 1, -1))
44+
self.assertEqual(r[-2:2:-1], range(1, 5))
45+
46+
def test_slice_offsetted_range(self):
47+
r = range(4, 16)
48+
self.assertEqual(r[:], range(4, 16))
49+
self.assertEqual(r[::-1], range(15, 3, -1))
50+
self.assertEqual(r[:4], range(4, 8))
51+
self.assertEqual(r[:-4], range(4, 12))
52+
self.assertEqual(r[4:], range(8, 16))
53+
self.assertEqual(r[-4:], range(12, 16))
54+
self.assertEqual(r[4:-4], range(8, 12))
55+
self.assertEqual(r[-4:4:-1], range(12, 8, -1))
56+
57+
def test_slice_overflow_range(self):
58+
r = range(8)
59+
self.assertEqual(r[2:200], range(2, 8))
60+
self.assertEqual(r[-200:-2], range(0, 6))
61+
62+
def test_stepped_slice_range(self):
63+
r = range(8)
64+
self.assertEqual(r[::2], range(0, 8, 2))
65+
self.assertEqual(r[::-2], range(7, -1, -2))
66+
self.assertEqual(r[:2:2], range(0, 2, 2))
67+
self.assertEqual(r[:-2:2], range(0, 6, 2))
68+
self.assertEqual(r[2::2], range(2, 8, 2))
69+
self.assertEqual(r[-2::2], range(6, 8, 2))
70+
self.assertEqual(r[2:-2:2], range(2, 6, 2))
71+
72+
def test_stepped_slice_stepped_range(self):
73+
r = range(0, 16, 2)
74+
self.assertEqual(r[::2], range(0, 16, 4))
75+
self.assertEqual(r[:2:2], range(0, 4, 4))
76+
self.assertEqual(r[:-2:2], range(0, 12, 4))
77+
self.assertEqual(r[2::2], range(4, 16, 4))
78+
self.assertEqual(r[-2::2], range(12, 16, 4))
79+
self.assertEqual(r[2:-2:2], range(4, 12, 4))
80+
81+
def test_rev_slice_range(self):
82+
r = range(8)
83+
self.assertEqual(r[::-1], range(7, -1, -1))
84+
self.assertEqual(r[:2:-1], range(7, 2, -1))
85+
self.assertEqual(r[:-2:-1], range(7, 6, -1))
86+
self.assertEqual(r[2::-1], range(2, -1, -1))
87+
self.assertEqual(r[-2::-1], range(6, -1, -1))
88+
self.assertEqual(r[-2:2:-1], range(6, 2, -1))
89+
r = range(0, 16, 2)
90+
self.assertEqual(r[::-2], range(14, -2, -4))
91+
self.assertEqual(r[:2:-2], range(14, 4, -4))
92+
self.assertEqual(r[:-2:-2], range(14, 12, -4))
93+
self.assertEqual(r[2::-2], range(4, -2, -4))
94+
self.assertEqual(r[-2::-2], range(12, -2, -4))
95+
self.assertEqual(r[-2:2:-2], range(12, 4, -4))
96+
97+
def test_slice_zero_step(self):
98+
msg = '^slice step cannot be zero$'
99+
with self.assertRaisesRegexp(ValueError, msg):
100+
range(8)[::0]
101+
102+
103+
if __name__ == '__main__':
104+
unittest.main()

0 commit comments

Comments
 (0)