Skip to content

Commit 05117c0

Browse files
committed
-
1 parent bf41943 commit 05117c0

File tree

2 files changed

+126
-23
lines changed

2 files changed

+126
-23
lines changed

python_toolbox/cute_iter_tools.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,28 +35,25 @@ def get_consecutive_subsequences(iterable, length=2, wrap_around=False):
3535

3636
iterator = iter(iterable)
3737

38-
first_items = get_items(iterator, length - 1, container=list)
38+
first_items = get_items(iterator, length)
3939
if wrap_around:
40-
len_first_items = len(first_items)
41-
xrange_len_first_items = xrange(len(first_items))
42-
if len(first_items) < length - 1:
43-
for i in xrange_len_first_items: # to
44-
yield (first_items[(i+j) % len_first_items] for j in
45-
xrange_len_first_items)
46-
else:
47-
iterator = itertools.chain(iterator, first_items)
40+
if len(first_items) < length:
41+
raise NotImplementedError(
42+
'`length` is greater than the length of the iterable, and '
43+
'`wrap_around` is set to `True`. Behavior for this is not '
44+
'implemented, because it would require repeating some members '
45+
'more than once.'
46+
)
47+
first_items_except_last = first_items[:-1]
48+
iterator = itertools.chain(iterator, first_items_except_last)
4849

4950
deque = collections.deque(first_items)
50-
deque.appendleft(object()) # Filler to be removed on first iteration.
51+
yield first_items
5152

5253
if not wrap_around:
5354
# If `wrap_around` is `False`, we avoid holding a reference to items in
5455
# `first_items`, because they may need to be garbage-collected:
55-
del first_items[:]
56-
# We empty the sequence instead of deleting it because deleting it at
57-
# this point would make for illegal Python syntax.
58-
59-
yield tuple(first_items)
56+
del first_items
6057

6158
for current in iterator:
6259
deque.popleft()

test_python_toolbox/test_cute_iter_tools/test_get_consecutive_subsequences.py

Lines changed: 114 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,136 @@
33

44
'''Testing module for `cute_iter_tools.get_consecutive_subsequences`.'''
55

6+
from python_toolbox import gc_tools
67
from python_toolbox import cute_testing
78
from python_toolbox import sequence_tools
89

910
from python_toolbox.cute_iter_tools import get_consecutive_subsequences
1011

1112

12-
def test():
13-
'''Test basic workings of `cute_iter_tools.get_consecutive_subsequences`.'''
13+
def test_length_2():
1414

15-
# `consecutive_pairs` returns an iterator, not a sequence:
16-
assert not sequence_tools.is_sequence(get_consecutive_subsequences(range(4)))
15+
# `get_consecutive_subsequences` returns an iterator, not a sequence:
16+
assert not sequence_tools.is_sequence(
17+
get_consecutive_subsequences(range(4))
18+
)
1719

1820
assert tuple(get_consecutive_subsequences(range(4))) == \
1921
tuple(get_consecutive_subsequences(xrange(4))) == \
2022
((0, 1), (1, 2), (2, 3))
2123

2224
assert tuple(get_consecutive_subsequences(range(4), wrap_around=True)) == \
23-
tuple(get_consecutive_subsequences(xrange(4), wrap_around=True)) == \
25+
tuple(get_consecutive_subsequences(xrange(4), wrap_around=True)) ==\
2426
((0, 1), (1, 2), (2, 3), (3, 0))
2527

2628
assert tuple(get_consecutive_subsequences('meow')) == \
2729
(('m', 'e'), ('e', 'o'), ('o', 'w'))
2830

29-
assert tuple(get_consecutive_subsequences([1], wrap_around=True)) == \
30-
((1, 1),)
31+
32+
def test_iterable_too_short():
33+
with cute_testing.RaiseAssertor(NotImplementedError):
34+
tuple(get_consecutive_subsequences([1], wrap_around=True))
35+
36+
37+
def test_various_lengths():
38+
assert tuple(get_consecutive_subsequences(xrange(7), length=3)) == \
39+
((0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6))
40+
assert tuple(get_consecutive_subsequences(xrange(7), length=4)) == \
41+
((0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6))
42+
assert tuple(get_consecutive_subsequences(xrange(7), length=5)) == \
43+
((0, 1, 2, 3, 4), (1, 2, 3, 4, 5), (2, 3, 4, 5, 6))
44+
45+
assert tuple(get_consecutive_subsequences(xrange(7), length=4,
46+
wrap_around=True)) == ((0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5),
47+
(3, 4, 5, 6), (4, 5, 6, 0), (5, 6, 0, 1), (6, 0, 1, 2))
48+
assert tuple(get_consecutive_subsequences(xrange(7), length=5,
49+
wrap_around=True)) == ((0, 1, 2, 3, 4), (1, 2, 3, 4, 5),
50+
(2, 3, 4, 5, 6), (3, 4, 5, 6, 0), (4, 5, 6, 0, 1), (5, 6, 0, 1, 2),
51+
(6, 0, 1, 2, 3))
52+
3153

32-
54+
def test_garbage_collection():
55+
56+
garbage_collected = set()
57+
58+
class GarbageNoter(object):
59+
def __init__(self, n):
60+
assert isinstance(n, int)
61+
self.n = n
62+
def __del__(self):
63+
garbage_collected.add(self.n)
64+
65+
iterable = (GarbageNoter(i) for i in xrange(7))
66+
67+
consecutive_subsequences_iterator = \
68+
get_consecutive_subsequences(iterable, length=3)
69+
70+
def assert_garbage_collected(indexes):
71+
gc_tools.collect()
72+
assert set(indexes) == garbage_collected
73+
74+
assert_garbage_collected(())
75+
next(consecutive_subsequences_iterator)
76+
assert_garbage_collected(())
77+
next(consecutive_subsequences_iterator)
78+
assert_garbage_collected((0,))
79+
next(consecutive_subsequences_iterator)
80+
assert_garbage_collected((0, 1))
81+
next(consecutive_subsequences_iterator)
82+
assert_garbage_collected((0, 1, 2))
83+
next(consecutive_subsequences_iterator)
84+
assert_garbage_collected((0, 1, 2, 3))
85+
with cute_testing.RaiseAssertor(StopIteration):
86+
next(consecutive_subsequences_iterator)
87+
assert_garbage_collected((0, 1, 2, 3, 4, 5, 6))
88+
89+
90+
91+
def test_garbage_collection_wrap_around():
92+
93+
garbage_collected = set()
94+
95+
class GarbageNoter(object):
96+
def __init__(self, n):
97+
assert isinstance(n, int)
98+
self.n = n
99+
def __del__(self):
100+
garbage_collected.add(self.n)
101+
102+
iterable = (GarbageNoter(i) for i in xrange(7))
103+
104+
consecutive_subsequences_iterator = \
105+
get_consecutive_subsequences(iterable, length=3, wrap_around=True)
106+
107+
def assert_garbage_collected(indexes):
108+
gc_tools.collect()
109+
assert set(indexes) == garbage_collected
110+
111+
assert_garbage_collected(())
112+
next(consecutive_subsequences_iterator)
113+
assert_garbage_collected(())
114+
next(consecutive_subsequences_iterator)
115+
assert_garbage_collected(())
116+
next(consecutive_subsequences_iterator)
117+
assert_garbage_collected(())
118+
next(consecutive_subsequences_iterator)
119+
assert_garbage_collected((2,))
120+
next(consecutive_subsequences_iterator)
121+
assert_garbage_collected((2, 3))
122+
next(consecutive_subsequences_iterator)
123+
assert_garbage_collected((2, 3, 4))
124+
next(consecutive_subsequences_iterator)
125+
assert_garbage_collected((2, 3, 4, 5))
126+
with cute_testing.RaiseAssertor(StopIteration):
127+
next(consecutive_subsequences_iterator)
128+
assert_garbage_collected((0, 1, 2, 3, 4, 5, 6))
129+
130+
131+
132+
133+
134+
135+
136+
137+
138+

0 commit comments

Comments
 (0)