Reputation: 1711
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
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
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
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