In querying an API that has a paginated list of unknown length I found myself doing essentially
def fetch_one(self, n):
data = json.load(urlopen(url_template % n))
if data is None:
self.finished = True
return
for row in data:
if row_is_weird(row):
self.finished = True
return
yield prepare(row)
def work(self):
n = 1
self.finished = False
while not self.finished:
consume(self.fetch_one(n))
n += 1
the split between work and fetch_one makes it very easy to test, but the signalling via instance variables means I can’t have more than one work going on at the same time, which sucks. I came up with what I think is a cleaner solution, but it involves an iterator with two “done” states, and I have no idea what to call it. I’m sure this pattern exists elsewhere, so I’d appreciate pointers (or reasons why this is stupid):
class Thing(object):
def __init__(self, gen):
self.gen = gen
self.finished = False
def __iter__(self):
return self
def __next__(self):
try:
v = next(self.gen)
except StopThisThing:
self.finished = True
raise StopIteration
else:
return v
next = __next__
which I’d then use like
@thinged
def fetch_one(self, n):
data = json.load(urlopen(url_template % n))
if data is None:
raise StopThisThing()
for row in data:
if row_is_weird(row):
raise StopThisThing()
yield prepare(row)
def work(self):
n = 1
while True:
one = self.fetch_one(n)
consume(one)
if one.finished:
break
n += 1
so what is this Thing I have created?
I think that you can avoid that by yielding something special.
I had to build my own runnable example, to show what I mean:
Output:
I like this more than
self.finishedor a decorator like the one you built, but I think that something better could still be found. (Maybe this answer could help you with that).Update: A much simplier solution might be to transform
fetch_oneinto a class that carries its ownfinisedflag.A decorator approach to this solution might be:
Basically you don’t care anymore how
fetch_oneworks, only if what yields is ok or not.Usage example: