Skip to content

Commit 7fa7c98

Browse files
committed
add slicing support to memo subscripting in memoized generators
1 parent af8da7a commit 7fa7c98

File tree

2 files changed

+39
-3
lines changed

2 files changed

+39
-3
lines changed

unpythonic/gmemo.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,24 @@ def __next__(self):
132132
if kind is _fail:
133133
raise value
134134
return value
135-
# Support the `collections.abc.Sequence` API for already-computed items
135+
# Support a subset of the `collections.abc.Sequence` API for already-computed items
136136
def __len__(self):
137137
return len(self.memo)
138138
def __getitem__(self, k):
139-
if k >= len(self.memo):
140-
raise IndexError(f"Attempted to access index {k} of memoized generator; only {len(self.memo)} items available (at least so far)")
139+
if not isinstance(k, (int, slice)):
140+
raise TypeError(f"Expected an int or slice index, got {type(k)} with value {repr(k)}")
141+
length = len(self.memo)
142+
if isinstance(k, slice):
143+
# For slices where at least one item raises an exception, we raise the
144+
# exception that is encountered first when walking the slice.
145+
lst = []
146+
for kind, value in self.memo[k]:
147+
if kind is _fail:
148+
raise value
149+
lst.append(value)
150+
return lst
151+
if k >= length or k < -length:
152+
raise IndexError(f"memoized generator index out of range; got {k}, with {len(self.memo)} items currently available")
141153
kind, value = self.memo[k]
142154
if kind is _fail:
143155
raise value

unpythonic/tests/test_gmemo.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ def gen():
9797
def gen():
9898
yield from range(5)
9999
g3 = gen()
100+
101+
# Any item that has entered the memo can be retrieved by subscripting.
102+
# len() is the current length of the memo.
100103
test[len(g3) == 0]
101104
next(g3)
102105
test[len(g3) == 1]
@@ -107,8 +110,29 @@ def gen():
107110
test[g3[0] == 0]
108111
test[g3[1] == 1]
109112
test[g3[2] == 2]
113+
114+
# Items not yet memoized cannot be retrieved from the memo.
110115
test_raises[IndexError, g3[3]]
111116

117+
# Negative indices work too, counting from the current end of the memo.
118+
test[g3[-1] == 2]
119+
test[g3[-2] == 1]
120+
test[g3[-3] == 0]
121+
122+
# Counting back past the start is an error, just like in `list`.
123+
test_raises[IndexError, g3[-4]]
124+
125+
# Slicing is supported.
126+
test[g3[0:3] == [0, 1, 2]]
127+
test[g3[0:2] == [0, 1]]
128+
test[g3[::-1] == [2, 1, 0]]
129+
test[g3[0::2] == [0, 2]]
130+
test[g3[2::-2] == [2, 0]]
131+
132+
# Out-of-range slices produce the empty list, like in `list`.
133+
test[g3[3:] == []]
134+
test[g3[-4::-1] == []]
135+
112136
with testset("memoizing a sequence partially"):
113137
# To do this, build a chain of generators, then memoize only the last one:
114138
evaluations = Counter()

0 commit comments

Comments
 (0)