How to set time limit on raw_input

前端 未结 6 1520
抹茶落季
抹茶落季 2020-11-22 05:52

in python, is there a way to, while waiting for a user input, count time so that after, say 30 seconds, the raw_input() function is automatically skipped?

6条回答
  •  既然无缘
    2020-11-22 06:28

    The signal.alarm function, on which @jer's recommended solution is based, is unfortunately Unix-only. If you need a cross-platform or Windows-specific solution, you can base it on threading.Timer instead, using thread.interrupt_main to send a KeyboardInterrupt to the main thread from the timer thread. I.e.:

    import thread
    import threading
    
    def raw_input_with_timeout(prompt, timeout=30.0):
        print(prompt, end=' ')    
        timer = threading.Timer(timeout, thread.interrupt_main)
        astring = None
        try:
            timer.start()
            astring = input(prompt)
        except KeyboardInterrupt:
            pass
        timer.cancel()
        return astring
    

    this will return None whether the 30 seconds time out or the user explicitly decides to hit control-C to give up on inputting anything, but it seems OK to treat the two cases in the same way (if you need to distinguish, you could use for the timer a function of your own that, before interrupting the main thread, records somewhere the fact that a timeout has happened, and in your handler for KeyboardInterrupt access that "somewhere" to discriminate which of the two cases occurred).

    Edit: I could have sworn this was working but I must have been wrong -- the code above omits the obviously-needed timer.start(), and even with it I can't make it work any more. select.select would be the obvious other thing to try but it won't work on a "normal file" (including stdin) in Windows -- in Unix it works on all files, in Windows, only on sockets.

    So I don't know how to do a cross-platform "raw input with timeout". A windows-specific one can be constructed with a tight loop polling msvcrt.kbhit, performing a msvcrt.getche (and checking if it's a return to indicate the output's done, in which case it breaks out of the loop, otherwise accumulates and keeps waiting) and checking the time to time out if needed. I cannot test because I have no Windows machine (they're all Macs and Linux ones), but here the untested code I would suggest:

    import msvcrt
    import time
    
    def raw_input_with_timeout(prompt, timeout=30.0):
        print(prompt, end=' ')    
        finishat = time.time() + timeout
        result = []
        while True:
            if msvcrt.kbhit():
                result.append(msvcrt.getche())
                if result[-1] == '\r':   # or \n, whatever Win returns;-)
                    return ''.join(result)
                time.sleep(0.1)          # just to yield to other processes/threads
            else:
                if time.time() > finishat:
                    return None
    

    The OP in a comment says he does not want to return None upon timeout, but what's the alternative? Raising an exception? Returning a different default value? Whatever alternative he wants he can clearly put it in place of my return None;-).

    If you don't want to time out just because the user is typing slowly (as opposed to, not typing at all!-), you could recompute finishat after every successful character input.

提交回复
热议问题