Amaterastis
Amaterastis

Reputation: 479

How to interrupt Python program on user input?

I am writing a program which executes in some standard fashion, unless the user inputs something in terminal, in which case the program should change the behavior, e.g.

while True:
    # do something here

def interrupt(user_input):
    if user_input == "some command":
        sys.exit(0) # e.g.

How to achieve this by not having input() in while loop?

Upvotes: 2

Views: 4103

Answers (1)

costaparas
costaparas

Reputation: 5237

The simplest interpretation here is to use a daemon thread.

To achieve this in Python, you can make use of multi-threading. For that, there is the threading module.

Your daemon would run in the background, by setting daemon=True when creating the thread.

Below is a simple example of that in action. Your main logic would go in the main function. The while True infinite loop listens for user input and terminates the program once the user input matches some predefined value ("kill" in this case).

import sys
import threading

def main():
    print('Main program')
    while True:
        pass
        # Rest of main program

main_thread = threading.Thread(name='main program',
                               target=main, daemon=True)
main_thread.start()
while True:
    if input().lower() == 'kill':
        print('Terminating program')
        # Add some possible cleanup code here
        sys.exit(0)

Take note of this important information from the Python documentation about how daemonic threads are stopped:

Daemon threads are abruptly stopped at shutdown. Their resources (such as open files, database transactions, etc.) may not be released properly. If you want your threads to stop gracefully, make them non-daemonic and use a suitable signalling mechanism such as an Event.

That said, this approach may suffice for your purposes. Depending on your program logic, if some cleanup code is needed, it could be done before exiting -- since once the sys.exit(0) is called, the daemonic thread will be killed abruptly.

A more general solution, however, is to make use of threading.Event.

This is quite a distinct approach to the above in that we create a regular, non-daemonic thread and pass an Event to it. This time, instead of abruptly exiting the program on user input, we set the internal flag of the Event.

In parallel, instead of a while True infinite loop, the main function uses the non-blocking is_set() to break the loop once the Event is set.

import threading

def main(e):
    print('Main program')
    while not e.is_set():
        pass
        # Rest of main program
    print('Exiting')
    # Add some possible cleanup code here

e = threading.Event()

main_thread = threading.Thread(name='main program',
                               target=main, args=(e,))
main_thread.start()

while True:
    if input().lower() == 'kill':
        print('Terminating program')
        e.set()
        break

main_thread.join()

This has two advantages over the daemonic approach described:

  1. You could easily have multiple Events and have your program behave in different ways, depending on those events. There is also the blocking Event.wait() you can use instead of Event.is_set().
  2. Any cleanup code can instead be managed directly by the main() function.

Upvotes: 5

Related Questions