you mean like that?
todo_iter = iter(todo)
while True:
try:
value = next(todo_iter)
except StopIteration:
break
process(value)
or like that?
todo_iter = iter(todo) # Note: assume "todo" does not contain None
while value := next(todo_iter, None):
process(value)
I'd say neither of those are as clean as a simple for loop:
for value in todo:
process(value)
and yes, that's the case of a personal preference, although I'd bet a lot of Python programmers will share that preference with me. That's what "code smell" means, after all - it's not a bug which is clearly incorrect, it's a code which is best avoided based on reviewer's personal experience.