Python Twisted与Cmd模块集成

6 投票
1 回答
2134 浏览
提问于 2025-04-17 08:40

我喜欢Python的TwistedCmd这两个库。我想把它们结合起来使用。

我已经实现了一些功能,但到目前为止,我还没弄明白怎么让按下Tab键时能自动补全,因为在Twisted的LineReceiver中,我看不到如何立即接收到Tab键的按下事件(而不需要按回车)。

这是我目前的代码:

#!/usr/bin/env python

from cmd import Cmd
from twisted.internet import reactor
from twisted.internet.stdio import StandardIO
from twisted.protocols.basic import LineReceiver

class CommandProcessor(Cmd):
    def do_EOF(self, line):
        return True

class LineProcessor(LineReceiver):
    from os import linesep as delimiter # makes newline work

    def __init__(self):
        self.processor = CommandProcessor()
        self.setRawMode()

    def connectionMade(self):
        self.transport.write('>>> ')

    def rawDataReceived(self, data):
        self.processor.onecmd(data)
        self.transport.write('>>> ')

StandardIO(LineProcessor())
reactor.run()

除了Tab补全,这个程序基本上是能工作的。我可以输入像“help”这样的命令,Cmd模块会打印出结果。但是我失去了Cmd模块那种很酷的Tab补全功能,因为Twisted是一次只处理一行。我试着把LineProcessor.delimiter设置为空字符串,但没有效果。也许我需要找Twisted中的其他部分来替代LineReceiver?或者有没有更简单的方法,能让我避免逐个处理每个字符呢?

我不能单独使用Cmd,因为我想把这个做成一个网络应用,其中一些命令会导致发送数据,而从网络接收数据是异步进行的(并且会显示给用户)。

所以无论是从上面的代码开始,还是从其他完全不同的地方开始,我都想在Python中构建一个友好的终端应用,能够响应网络事件和Tab补全。我希望能利用现有的东西,而不需要自己实现太多功能。

1 个回答

9

你在这个方法上遇到了一些困难:

  • Cmd.onecmd 不会处理按键补全的功能。
  • 即使它能处理,你的终端也需要处于一种特殊模式,才能让每次按键都能被Python解释器接收到(可以用tty.setcbreak来实现这个)。
  • 正如你所知道的,Cmd.cmdloop 不会感知事件,它会一直等待输入。
  • 而且,要实现你想要的那些酷炫的行编辑功能,Cmd(实际上是readline)需要直接访问输入和输出。

考虑到这些困难,你可能想让CommandProcessor在自己的线程中运行。比如:

#!/usr/bin/env python

from cmd import Cmd
from twisted.internet import reactor

class CommandProcessor(Cmd):
    def do_EOF(self, line):
        return True

    def do_YEP(self, line):
        reactor.callFromThread(on_main_thread, "YEP")

    def do_NOPE(self, line):
        reactor.callFromThread(on_main_thread, "NOPE")

def on_main_thread(item):
    print "doing", item

def heartbeat():
    print "heartbeat"
    reactor.callLater(1.0, heartbeat)

reactor.callLater(1.0, heartbeat)
reactor.callInThread(CommandProcessor().cmdloop)
reactor.run()

撰写回答