Repository navigation
stdout isnt doesn't unwraped when interupted #212
Description
Activity
Unfortunately, as far as I am aware, the progressbar can't detect these exceptions since they don't happen in code managed by the progressbar.
An alternative that does work properly is by using the
withstatement. Thewithstatement was specifically designed for cases like these:import progressbar import time with progressbar.ProgressBar(redirect_stdout=True) as bar: for i in bar(range(100)): print('Some text', i) time.sleep(0.02) if i == 50: raise KeyboardInterrupt
I ran with the same problem with stderr and using
progressbar.streams.wrap_stderr. I did not know that using the progress bar as a context manager would prevent this problem (so I created a context manager wrapper myself). I could not find this feature in the doc, I think it would be nice to mention it somewhere, don't you think?I ran with the same problem with stderr and using
progressbar.streams.wrap_stderr. I did not know that using the progress bar as a context manager would prevent this problem (so I created a context manager wrapper myself). I could not find this feature in the doc, I think it would be nice to mention it somewhere, don't you think?It's mentioned in the readme part of the docs: https://progressbar-2.readthedocs.io/en/latest/#context-wrapper
And there are a load of examples that use it: https://progressbar-2.readthedocs.io/en/latest/examples.htmlI agree that the docs can be improved a lot though. The big problem is that my time is quite limited and this project is not the only project that I maintain: https://pypi.org/user/WoLpH/
If you have suggestions where the docs can be improved, all help is appreciated :)@wolph
Thanks for pointing out the context manager solution,
In my case, I'm handing around a lot of iterators and I'd like to selectively wrap some in progress bars, so the context manager would be inconvenient to use.I agree, context managers are often inconvenient to work with and your
bar.finish(dirty=True)is an easier solution in most cases.I think that the iterator version could also clean up after itself, by ensuring that finished() is called when a general exceptions would stop the iterator.
I fail to see how, but I might be missing something here :)
Is there a disadvantage to this kind of solution I'm not seeing?
Your code still uses the
bar.finish(...)in theexcept:part. Without that it still won't work.I also overrode the
__exit__method here, which means that when the context manager is being used and the iteration is interrupted, the progress bar doesn't incorrectly show a completed state.That's indeed a bug that needs to be fixed. Good catch!
Nevermind... all that, thanks to your suggestion I found the answer. I can catch
GeneratorExit. Since it doesn't inheritExceptionI never knew I could catch that from the code.Thanks for the suggestion, I'll fix it soon :)
I've added the two changes to the development branch, can you test if that does everything you were looking for? :)
I'm always happy with pull requests if you have more suggestions of course :)
@mmcewen-g not sure if you noticed the update so pinging you again :)
If it works ok I'll create a new release
Thank you for writing this pedagogically!
I realized that we could do a less invasive job by promoting the ProgressBar itself to being a Generator. This is a much smaller change, as a Generator is just a special type of Iterator, which the ProgressBar is already. It also means the current examples and code usage can stay identical, and that we aren't introducing any new objects.
This is what I eventually did in my project using ProgressBar:
def progress_bar(iterator, *args, **kwargs): with progressbar.ProgressBar(*args, **kwargs) as progress: for item in progress(iterator): yield item
Which is very close to what
progressbar.shortcuts.progressbardoes.6 remaining items
- added a commit that references this issue
on Feb 2, 2025 - added 7 commits that reference this issue
on Jun 20, 2026 Fixed on
develop: iterating a bar is now a generator, so abandoning the loop (e.g.KeyboardInterrupt) triggersGeneratorExit→finish(dirty=True), which restores the redirected streams. Locked end-to-end bytests/test_stream.py::test_redirect_stdout_unwrapped_after_keyboard_interrupt(subprocess, assertssys.stdoutis unwrapped) added in #326. Closing.- added a commit that references this issue
on Jul 19, 2026 - added a commit that references this issue
on Aug 2, 2026
When redirect_stdout is beign used, and the iterator is interrupted, for example by a KeyboardInterrupt, stdout remains wrapped,
Example code
You'll end up with the 50% completed progress bar sitting on your terminal and refusing to leave.
When an exception stops the progress of the iterator, the progress bar doesn't take notice and unwrap stdout, as it does when finished() is called.
Manually wrapping the iteration in a try:except: works, but obviously isn't very nice
Versions
Python 3.7.3, IPython 7.8.0, Linux, progressbar version 3.47.0