Balz Guenat
Balz Guenat

Reputation: 1711

How do I let the user pause a python script at predefined points?

Say I have lots of work to do, so I want to give the user the option to pause and/or halt the work. But I don't want to end up with an inconsistent state so I can only pause and exit at some points in the program.

How would I implement a user_requested_pause() method that I could use as below?

for item in some_very_large_list:
  do_work()
  # can't stop here
  do_more_work()
  # now we could pause.
  if user_requested_pause():
    # ask user how to proceed, then continue or exit with consistent state

I thought about just using ctrl+c but the KeyboardInterrupt can occur at any point.

Ideally, I would check if there is input available on stdin but I haven't found a way to do that. read() just blocks if there's no input which would pause the program even if the user didn't want it to.

Edit: I solved it as follows:

import signal
pause_requested = False
def handler(signum, frame):
  global pause_requested
  pause_requested = True
for item in some_very_large_list:
  do_work()
  do_more_work()
  if pause_requested:
    # ask user how to proceed, then continue or exit with consistent state
    pause_requested = False

Upvotes: 3

Views: 693

Answers (4)

Aaron
Aaron

Reputation: 11075

The best solution if you want to have execution continue without user intervention (instead of pausing by default requiring the user to re-start) requires the use of a secondary thread, coroutine, or process to accept user input and set some sort of flag telling the main processing thread to pause. This can be accomplished in quite a number of ways, but I will demonstrate using only python standard library and builtin functions. Other methods may be more flexible (like detecting a specific keypress or using a graphical interface rather than command line) but will quickly run into compatibility issues as detecting hardware input is usually done with a GUI library and is usually slightly different between systems. Some libraries like keyboard are pretty good, but even this one has a caveat (requires root on linux).

example code using a thread to wait for input() function (user presses enter):

import threading, time

class user_input_thread(threading.Thread):
    def __init__(self, event_obj):
        super().__init__()
        self.daemon = True
        self.event_obj = event_obj

    def run(self):
        while True:

            self.event_obj.wait() #wait for the user to resume processing from main thread
            print('process resumed')
            input('press enter to pause\n') #use raw_input() for python 2.x
            print('process will pause')
            self.event_obj.clear() #clear the flag forcing processing to stop

def main_work(event_obj, *args):
    for x in range(10):
        if not event_obj.is_set():
            input('press enter to resume') #use raw_input() for python 2.x
            event_obj.set()
        time.sleep(2) #heavy computation that can't be interrupted
        print('done with task {}'.format(x))

if __name__ == '__main__':
    event_obj = threading.Event() #work starts paused
    user_input_thread(event_obj).start()
    main_work(event_obj)

Due to some of the limitations of pure python this is not a perfect solution as the thread (even though it's a daemon) will not exit at the end of the program as it is waiting on user input still. This requires you to press enter to terminate the program (although all the work will be done just fine and the interpreter will just be waiting at the end of the file). Additional user instructions via print statements and some extra control logic could make this better, but would not largely change the functionality. Another option would just be to put a try: except: in a loop in the main file which sets/clears a flag that a worker thread doing the main work checks every so often.

Upvotes: 0

J Lossner
J Lossner

Reputation: 139

You could use multithreading to have a separate thread for doing work, and regularly checking on a state variable requested_pause. The main thread then runs in a loop of asking for use input(), and sets requested_pause accordingly when there is an entry.

Upvotes: 1

Solomon Ucko
Solomon Ucko

Reputation: 6109

You may be able to use the signal module for this, by making the handler set a flag to tell the program to pause and wait for input.

Upvotes: 1

l0ckky
l0ckky

Reputation: 11

You can look into the asyncio library. Specifically if you have an awaitable function to catch the keyboard input.

Upvotes: 1

Related Questions