Python3:readline 等效于 select.select()

Dev*_*son 5 python gnu readline python-3.x

在 Python 脚本中,程序员可以导入 readline,然后为 input() 提供扩展功能(readline 还有许多其他用途)。我想在我的脚本中使用 select.select() 而不是 input() 因为我喜欢超时功能。但是,当导入 readline 时,我无法使用 input() 通过 readline 获得的功能。我指的“扩展功能”的一个示例是能够按向上键并查看先前的输入,或使用左右箭头键移动内联光标以对输入进行更改。

问题:如何使 select.select() 具有 GNU-readline 功能?这可能吗?

编辑:为了防止你们中的任何人对我想要完成的任务感到好奇,我制作了一个基于终端的聊天机器人(有点像 Alicebot)。如果该位在设定的时间内没有收到任何输入,我希望机器人感到无聊并做其他事情。(https://launchpad.net/neobot

syn*_*tel 4

您可以使用 readline 模块的 readline.set_pre_input_hook([function]) 机制来完成此操作。

下面是一个在 10 秒无输入后超时的示例 - 未实现的是在提供输入时禁用警报的机制。

对于线程,这必须以不同的方式完成,因为信号无法遍历线程。但是,你明白了基本的想法..

我对这段代码提前表示歉意,我在一家咖啡店里用我的笔记本电脑,只是把它拼凑在一起。这是python2.7代码,但基本上应该与python3兼容——概念是重要的部分。

我认为如果您希望每行输入都有超时,您可能需要将警报禁用放在 input_loop() 函数开头的某个位置。

您还应该查看 Python 模块树中的 readline.c 源代码以获取更多想法。

#!/usr/bin/python

import readline
import logging
import signal
import os

LOG_FILENAME = '/tmp/completer.log'
HISTORY_FILENAME = '/tmp/completer.hist'

logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG,)

class YouAreTooSlow(Exception): pass


def get_history_items():
    return [ readline.get_history_item(i)
             for i in xrange(1, readline.get_current_history_length() + 1)
             ]

class HistoryCompleter(object):

    def __init__(self):
        self.matches = []
        return

    def complete(self, text, state):
        response = None
        if state == 0:
            history_values = get_history_items()
            logging.debug('history: %s', history_values)
            if text:
                self.matches = sorted(h 
                                      for h in history_values 
                                      if h and h.startswith(text))
            else:
                self.matches = []
            logging.debug('matches: %s', self.matches)
        try:
            response = self.matches[state]
        except IndexError:
            response = None
        logging.debug('complete(%s, %s) => %s', 
                      repr(text), state, repr(response))
        return response

def input_loop():
    if os.path.exists(HISTORY_FILENAME):
        readline.read_history_file(HISTORY_FILENAME)
    print 'Max history file length:', readline.get_history_length()
    print 'Startup history:', get_history_items()
    try:
        while True:
            line = raw_input('Prompt ("stop" to quit): ')
            if line == 'stop':
                break
            if line:
                print 'Adding "%s" to the history' % line
    finally:
        print 'Final history:', get_history_items()
        readline.write_history_file(HISTORY_FILENAME)

# Register our completer function

def slow_handler(signum, frame):
    print 'Signal handler called with signal', signum
    raise YouAreTooSlow()

def pre_input_hook():
    signal.signal(signal.SIGALRM, slow_handler)
    signal.alarm(10)

readline.set_pre_input_hook(pre_input_hook)
readline.set_completer(HistoryCompleter().complete)

# Use the tab key for completion
readline.parse_and_bind('tab: complete')

# Prompt the user for text
input_loop()
Run Code Online (Sandbox Code Playgroud)