When executing a long running task, it is often useful to provide some feedback to the user. In a console program, this often consists of text such as "Running...." where a dot is printed (say) each second.
Adding a progress indicator like this to existing code can be tricky, but with threads and a context manager, it's easy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
from __future__ import with_statement import threading import sys # Implementation of Ticker class class Ticker(threading.Thread): def __init__(self, msg): threading.Thread.__init__(self) self.msg = msg self.event = threading.Event() def __enter__(self): self.start() def __exit__(self, ex_type, ex_value, ex_traceback): self.event.set() self.join() def run(self): sys.stdout.write(self.msg) while not self.event.isSet(): sys.stdout.write(".") sys.stdout.flush() self.event.wait(1) # Here's how we use it... if __name__ == '__main__': import time with Ticker("A test"): time.sleep(10) with Ticker("Second test"): time.sleep(5) raise Exception("Bang!")
To execute a block of code with a ticker display, simply enclose the block in a "with Ticker(msg)" statement. The class itself will take care of starting up and stopping the ticker thread, catching and reraising exceptions, etc.
If you want to, you can extend the class, by providing additional information, such as estimated time to completion. This would require additional methods to tell the ticker how far through we have reached. You can then use "with Ticker(msg) as tick" to get a reference to the ticker (you need __enter__ to return self in that case).
The idea was suggested by a posting on the haskell-cafe list by Ben Franksen, giving an implementation of a similar idea in Haskell.