Skip to content

stdout isnt doesn't unwraped when interupted #212

Description

When redirect_stdout is beign used, and the iterator is interrupted, for example by a KeyboardInterrupt, stdout remains wrapped,

Example code

import progressbar 
import time 
for i in progressbar.progressbar(range(100), redirect_stdout=True): 
    print('Some text', i) 
    time.sleep(0.1) 
    if i == 50:
        raise KeyboardInterrupt

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

import progressbar 
import time 
bar = progressbar.bar.ProgressBar(redirect_stdout=True) 
try:
    for i in bar(range(100)): 
        print('Some text', i) 
        time.sleep(0.1) 
        if i==50: 
            raise KeyboardInterrupt 
except:
    bar.finish(dirty=True) 
    raise

Versions

Python 3.7.3, IPython 7.8.0, Linux, progressbar version 3.47.0

Activity

  1. wolph commented on Nov 2, 2019

    @wolph
    Owner

    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 with statement. The with statement 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
  2. Neraste commented on Nov 3, 2019

    @Neraste

    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?

  3. wolph commented on Nov 4, 2019

    @wolph
    Owner

    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.html

    I 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 the except: 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!

  4. wolph commented on Nov 5, 2019

    @wolph
    Owner

    Nevermind... all that, thanks to your suggestion I found the answer. I can catch GeneratorExit. Since it doesn't inherit Exception I never knew I could catch that from the code.

    Thanks for the suggestion, I'll fix it soon :)

  5. reopened this on Nov 5, 2019
  6. wolph commented on Nov 5, 2019

    @wolph
    Owner

    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 :)

  7. wolph commented on Nov 6, 2019

    @wolph
    Owner

    @mmcewen-g not sure if you noticed the update so pinging you again :)

    If it works ok I'll create a new release

  8. Neraste commented on Nov 9, 2019

    @Neraste

    Thank you for writing this pedagogically!

  9. Neraste commented on Nov 30, 2019

    @Neraste

    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.progressbar does.

  10. 6 remaining items

  11. reopened this on Aug 30, 2023
  12. added a commit that references this issue on Feb 2, 2025
    d038ce8
  13. wolph commented on Jul 18, 2026

    @wolph
    Owner

    Fixed on develop: iterating a bar is now a generator, so abandoning the loop (e.g. KeyboardInterrupt) triggers GeneratorExit → finish(dirty=True), which restores the redirected streams. Locked end-to-end by tests/test_stream.py::test_redirect_stdout_unwrapped_after_keyboard_interrupt (subprocess, asserts sys.stdout is unwrapped) added in #326. Closing.

  14. added a commit that references this issue on Jul 19, 2026
    62c5b45
  15. added a commit that references this issue on Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions