The current docstring in the Spinner class demonstrates using s.next() to update the spinner. But this is a Python 2-ism that no longer exists for iterators in Python 3. Instead the correct documentation would be next(s).
Additionally, I think it's confusing that the API for Spinner is so different from ProgressBar and ProgressBarOrSpinner. In the latter two cases, their __enter__ method (when using with ProgressBar(...) as bar: returns the ProgressBar(OrSpinner) instance itself, which is iterable itself. It also provides an update() method which can be called to update the progress bar. Whereas with Spinner(...) as s: does not return the Spinner instance itself, but rather a generator.
It should be easy to update the API for Spinner (make it iterable, make __enter__ return the Spinner instance, and add an update() method) so that it is more like ProgressBar.
I would suggest making the updates to Spinner and fixing the documentation at the same time.
I'm on it
So. If we make the spinner iterable, It doesn't have a stop condition. So if the user decides to make an adventerous loop (like below) we will not go to space today
for s in console.Spinner("Reticulating splines", file=stream, chars=chars):
foo_bar()
Perhaps it's better to just rewrite the generator into an update() function (while leaving the __enter__ __exit__ methods)? Or should we not worry about this use case.
That's a good point and exposes another inconsistency between Spinner and ProgressBar, where the latter can take a length/iterator as an argument. This makes sense since a progress bar is more for something with a known amount of work, while a spinner is for an unknown amount of work. Though sometimes iterators are non-finite, and it would be useful to wrap such an iterator in a spinner.
That said, your for loop example is still useful (though it would also be used with a with statement to properly cancel the spinner when done, like:
with Spinner('message') as spin:
for _ in spin:
# do stuff
break
This is equivalent to having a while True: loop that you eventually have to break out of, except it updates the spinner while looping.
Meanwhile having an update() method would just be equivalent to calling next(spinner).
Basically this would be matter of:
Spinner.__init__ so it does self._iter = self._iterator() (or self._silent_iterator)__enter__ to just return self and also add __iter__ which does the same (return self)__next__ which takes next(self._iter)update() which calls next(self)