1

I have the following code to get user input to do certain tasks. What I want is to break out of the user prompt when some other task, outside this module, is completed. So even though the shell is showing 'Enter Command:', I want it to stop waiting for a prompt from the user and continue to the other task. Is this even possible and if so, how? Thanks in advance.

    def get_user_input():
        while True:
            info = str(raw_input('Enter Command:'))
            if info == 'exit':
                break
            if info == 'do_something':
                do_something()

Edit: Actually this is already in a thread and the thread closes correctly. The issue is the screen is stuck on the user prompt even though the user prompt is useless at that point since the thread calling for the input is already closed. Any suggestions?

saanity
  • 23
  • 5
  • 3
    You could put it in a *thread* and kill the thread, but actually it is rather weird to ask a user for input and then all of a sudden change your mind... – Willem Van Onsem Jan 27 '17 at 21:13
  • you can't do this with `raw_input()`. Maybe if you run it in thread then you could do something with this. – furas Jan 27 '17 at 21:13
  • 1
    @furas: I think technically you can if you wrap the code in a thread and call `kill` on it, but as said, it is very inelegant (and well "rude" I would say). – Willem Van Onsem Jan 27 '17 at 21:14
  • 1
    Agree that the correct answer is "don't do this". In addition, the problem with killing the thread, is how do you know if the thread is waiting input versus processing "do_something()"? Better to implement a "getch" type function that loops through key presses. Check out https://rosettacode.org/wiki/Keyboard_input/Keypress_check#Python But really the entire answer to your question is the `threading` module or `multiprocessing` module that allow you do do stuff in parallel with some communication between processes. – RobertB Jan 27 '17 at 21:18
  • 1
    @WillemVanOnsem I think OP want to do something similar to message window with button "Cancel Action" in GUI, so maybe `Ctrl+C` could be better than `raw_input()` and command `exit`. – furas Jan 27 '17 at 21:19
  • @RobertB: well I was not defending the idea at all. Just stating that it was technically possible doing that... – Willem Van Onsem Jan 27 '17 at 21:20
  • you cannot easily kill a thread. checkout this [so question](http://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python) – ShmulikA Jan 27 '17 at 22:01

2 Answers2

1

Using Signals

you can use signal module (linux / unix only)

import signal


class UserInputTimeoutError(Exception):
    pass


def handler(signum, frame):
    raise UserInputTimeoutError('no input from user')

# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)

try:
    # This may hang indefinitely
    user_input = raw_input('please insert something here:')
    print('got %s from user' % user_input)

    # cancel the alarm signal
    signal.alarm(0)
except UserInputTimeoutError:
    print('\nno input from user')

output when input is 'hello':

please insert something here:hello
got hello from user

output when no input for 5 seconds:

please insert something here:
no input from user


Using Select

another option is to use select.select() for non-blocking io operation.

the get_user_input function tries to read every 0.1 sec the sys.stdin, if there is data to read it reads single byte. if it encounters a new line it returns the string.

if the timeout passed we exit returning None

the code:

import select
import sys
import time


def get_user_input(msg, timeout=5):
    print(msg)
    user_input = []
    stime = time.time()

    while time.time() - stime <= timeout:
        readables, _, _ = select.select([sys.stdin], [], [], 0.1)
        if not readables:
            continue
        chr = readables[0].read(1)
        if chr == '\n':
            return ''.join(user_input)
        user_input.append(chr)

user_input = get_user_input('please insert something:')

if user_input is None:
    print('no user input')
else:
    print('input is %s' % user_input)

example output for input hello select:

please insert something:
hello select
input is hello select

example when no input for 5 seconds:

please insert something:
no user input

Windows support

if you use windows for some reason you can check out this answer about msvcrt module.

basically its the same as select.select for non-blocking io using msvcrt.kbhit() to check if user returned input.

if you need more info please update about your OS.

Community
  • 1
  • 1
ShmulikA
  • 3,468
  • 3
  • 25
  • 40
  • Thank you so much. Using a timeout, I can check for an outside condition and break out of that while loop. Much appreciated. – saanity Jan 27 '17 at 21:57
0

I think you can use this answer to read until a timeout, then check a variable and exit if the variable is set to false. If not then attempt to read again.

https://stackoverflow.com/a/2904057/2066459

import sys, select

print "Answer me!"
while var:
    i, o, e = select.select( [sys.stdin], [], [], 10 )

    if (i):
      print "You said", sys.stdin.readline().strip()
      break
Community
  • 1
  • 1
Robert Jacobs
  • 3,266
  • 1
  • 20
  • 30