Как установить ограничение по времени на raw_input [дубликат]

21
задан abccd 18 May 2017 в 04:24
поделиться

2 ответа

Функция signal.alarm, на которой основано рекомендуемое @jer решение, к сожалению, работает только под Unix. Если вам нужно кроссплатформенное или Windows-специфичное решение, вы можете основать его на threading.Timer, используя thread.interrupt_main для отправки KeyboardInterrupt в основной поток из потока таймера. Т.е. :

import thread
import threading

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

это вернет None независимо от того, истекут ли 30 секунд или пользователь явно решит нажать control-C, чтобы отказаться от ввода чего-либо, но, кажется, нормально рассматривать эти два случая одинаково (если вам нужно различать, вы можете использовать для таймера собственную функцию, которая, перед прерыванием основного потока записывает где-то факт того, что таймаут произошел, и в своем обработчике для KeyboardInterrupt обращаться к этому "где-то", чтобы отличить, какой из двух случаев произошел).

Edit: Я мог бы поклясться, что это работает, но, должно быть, я ошибся - в приведенном выше коде отсутствует явно необходимый timer.start(), и даже с ним я не могу заставить его работать дальше. select.select был бы очевидной другой вещью, чтобы попробовать, но он не будет работать на "нормальном файле" (включая stdin) в Windows - в Unix он работает на всех файлах, в Windows только на сокетах.

Так что я не знаю, как сделать кроссплатформенный "raw input with timeout". Специфичный для windows можно построить с помощью узкого цикла, опрашивающего msvcrt.kbhit, выполняющего msvcrt.getche (и проверяющего, не является ли это возвратом, указывающим на завершение вывода, в этом случае он выходит из цикла, иначе накапливается и продолжает ждать) и проверяющего время тайм-аута, если это необходимо. Я не могу проверить, потому что у меня нет машины с Windows (все они Mac и Linux), но вот непроверенный код, который я бы предложил:

import msvcrt
import time

def raw_input_with_timeout(prompt, timeout=30.0):
    print prompt,    
    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

ОП в комментарии говорит, что не хочет возвращать None по таймауту, но какова альтернатива? Вызвать исключение? Возвращение другого значения по умолчанию? Какую бы альтернативу он ни хотел, он явно может поставить ее вместо моего return None;-).

Если вы не хотите делать тайм-аут только потому, что пользователь печатает медленно (в отличие от того, что не печатает вообще!), вы можете пересчитывать finishat после каждого успешного ввода символа.

28
ответ дан 29 November 2019 в 20:09
поделиться

Я нашел решение этой проблемы в сообщении блога . Вот код из этого сообщения в блоге:

import signal

class AlarmException(Exception):
    pass

def alarmHandler(signum, frame):
    raise AlarmException

def nonBlockingRawInput(prompt='', timeout=20):
    signal.signal(signal.SIGALRM, alarmHandler)
    signal.alarm(timeout)
    try:
        text = raw_input(prompt)
        signal.alarm(0)
        return text
    except AlarmException:
        print '\nPrompt timeout. Continuing...'
    signal.signal(signal.SIGALRM, signal.SIG_IGN)
    return ''

Обратите внимание: этот код будет работать только в ОС * nix .

13
ответ дан 29 November 2019 в 20:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: