Search code examples
pythonprintingprogress-bariterabletqdm

tqdm: extract time passed + time remaining?


I have been going over the tqdm docs, but no matter where I look, I cannot find a method by which to extract the time passed and estimated time remaining fields (basically the center of the progress bar on each line: 00:00<00:02).

 0%|          | 0/200 [00:00<?, ?it/s]
  4%|▎         | 7/200 [00:00<00:02, 68.64it/s]
  8%|▊         | 16/200 [00:00<00:02, 72.87it/s]
 12%|█▎        | 25/200 [00:00<00:02, 77.15it/s]
 17%|█▋        | 34/200 [00:00<00:02, 79.79it/s]
 22%|██▏       | 43/200 [00:00<00:01, 79.91it/s]
 26%|██▌       | 52/200 [00:00<00:01, 80.23it/s]
 30%|███       | 61/200 [00:00<00:01, 82.13it/s]
....
100%|██████████| 200/200 [00:02<00:00, 81.22it/s]

tqdm works via essentially printing a dynamic progress bar anytime an update occurs, but is there a way to "just" print the 00:01 and 00:02 portions, so I could use them elsewhere in my Python program, such as in automatic stopping code that halts the process if it is taking too long?


Solution

  • Edit: see the library maintainer's answer below. Turns out, it is possible to get this information in the public API.


    tqdm does not expose that information as part of its public API, and I don't recommend trying to hack your own into it. Then you would be depending on implementation details of tqdm that might change at any time.

    However, that shouldn't stop you from writing your own. It's easy enough to instrument a loop with a timer, and you can then abort the loop if it takes too long. Here's a quick, rough example that still uses tqdm to provide visual feedback:

    import time
    from tqdm import tqdm
    
    
    def long_running_function(n, timeout=5):
        start_time = time.time()
    
        for _ in tqdm(list(range(n))):
            time.sleep(1)  # doing some expensive work...
            elapsed_time = time.time() - start_time
            if elapsed_time > timeout:
                raise TimeoutError("long_running_function took too long!")
    
    
    long_running_function(100, timeout=10)
    

    If you run this, the function will stop its own execution after 10 seconds by raising an exception. You could catch this exception at the call site and respond to it in whatever way you deem appropriate.


    If you want to be clever, you could even factor this out in a tqdm-like wrapper like this:

    def timed_loop(iterator, timeout):
        start_time = time.time()
        iterator = iter(iterator)
    
        while True:
            elapsed_time = time.time() - start_time
            if elapsed_time > timeout:
                raise TimeoutError("long_running_function took too long!")
    
            try:
                yield next(iterator)
            except StopIteration:
                pass
    
    
    def long_running_function(n, timeout=5):
        for _ in timed_loop(tqdm(list(range(n))), timeout=timeout):
            time.sleep(0.1)
    
    
    long_running_function(100, timeout=5)