2

I am calling a function (that can't be modified) to analyze data and it could take anywhere from minutes to hours to complete. I am trying to print the status/progress periodically to let the user know that the analysis is still progressing.

Since:

  1. I don't know how long the analysis takes a priori and
  2. I am restricted to using Python's standard library

then using the progressbar package wouldn't work. I am able to achieve this using multiprocessing:

import multiprocessing.pool import ThreadPool
import time

def analysis():
    #Perform analysis that takes a long time and cannot be modified

pool = ThreadPool(processes=2)
t = pool.apply_async(func = analysis)
start_time = time.time()
while not t.ready():
    print "Analyzing..."
    time.sleep(2)

It would be great if I could avoid using multiprocess and it could simply update in-place like the linux "watch" command.

slaw
  • 6,591
  • 16
  • 56
  • 109

1 Answers1

3

Not entirely sure what you are asking. If you want the "still analyzing" message to update in-place instead of adding line after line to the output, you might try something like this:

spinner = itertools.cycle("|/~\\")
while not t.ready():
    sys.stdout.write("\r%s: still analyzing %s" % (time.ctime(), next(spinner)))
    sys.stdout.flush()
    time.sleep(.5)

Here, the \r character (carriage return) is used to reset the output position to the beginning of the line. This will periodically update the line with the current time and a "spinner" animation.

In Python 3, you can do the same with the print function's parameters. For a more complex output, you might consider using curses. See this for a small example.


If you want to do this for multiple functions, or for many calls to the same function, you could also make this a decorator. Then the function will be executed in this way each time you call it. This still uses processes, but now they are hidden from the user.

def monitor(f):
    def f_():
        pool = ThreadPool(processes=2)
        t = pool.apply_async(func=f)
        spinner = itertools.cycle("|/~\\")
        while not t.ready():
            sys.stdout.write("\r%s: running %s %s" % (time.ctime(), f.__name__, next(spinner)))
            sys.stdout.flush()
            time.sleep(.5)
        sys.stdout.write("\n")
    return f_

@monitor
def analysis():
    time.sleep(4)

analysis()

Of course, this is a very simple version, that does support neither parameters nor a return value. To be honest, I haven't worked with threading in Python till now, and I don't want to show anything wrong.

Community
  • 1
  • 1
tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • I think this answers the second part of my question. I should've been clearer but is there a way to execute the function and print the "still analyzing" without using multiprocessing? – slaw Feb 03 '15 at 00:59
  • @slaw Do you mean, without using multiprocessing at all, or without explicitly using it? I guess you could wrap all this inside a decorator function... – tobias_k Feb 03 '15 at 08:36
  • Yes, without multiprocessing – slaw Feb 05 '15 at 11:20