[Twisted-Python] stdio.StandardIO, ServerProtocol and Services.
kgi
iacovou at gmail.com
Fri Oct 5 14:45:13 EDT 2007
Hi all.
I'm trying to write a "terminal" component, wrapped in a
service.Service, to drop into an application framework so that I can
use it to build interactive command-line tools. I've almost, but not
quite, got it working.
I know about manhole, but I want to write a simple command-based
syntax, rather than exposing a python namespace and interpreter. I
could also just go with a telnet or ssh interface, but that would
require an additional step (the actual telnet or ssh). There's also
stdiodemo.py, but that doesn't offer readline-like functionality,
which would be a Very Good Thing.
(Besides, I'm really curious as to why this approach is not working!)
The code below is a minimal stripped-down example of what I've got so
far through frantic grepping and glimpseing through the Twisted
codebase. (although there are some fragmentary holdovers from various
experiments; ignore these). At the moment, the command handler (the
lineReceived() method) just echoes the argument with some '+' signs
prepended.
The termios stuff comes from twisted/conch/stdio.py, via a mailing
list post saying something to the effect of, "this is the minimal
amount you have to do in order to hook up your terminal to your
process's stdio"; I don't have the ML link handy right now, sorry.
Note that whereas stdio.py passes a Protocol argument to
ServerProtocol, I've created an intermediate class, CLIServerProtocol,
which has a protocolFactory attribute. An examination of
twisted/conch/insults/insults.py shows that this should be equivalent.
For the curious bystanders, HistoricRecvLine is derived as follows:
HistoricRecvLine -> RecvLine -> TerminalProtocol
... and TerminalProtocol implements things like connectionMade, etc.
Here's the behaviour I see:
With the code as is, I can type in lines and get a reply from
lineReceived. However, none of the cursor keys or other things for
which there is special code in twisted/conch/recvline.py work; I just
get control codes all over the terminal.
If I uncomment the "reactor.run()", however, the behaviour changes
completely, and becomes much closer to what I expect: keypresses like
Home, cursor keys, etc, are honoured, as well as the history. However,
Ctrl-C merely loses the connection, and the terminal hangs and I have
to kill the twistd process externally.
The fact that adding reactor.run() (which shouldn't be needed in a
Service, right?) implies to me that the reactor isn't starting up
properly without it, and I can't see why.
Any advice would be greatly appreciated.
Ricky
--
import os, tty, sys, termios
from twisted.application import service
from twisted.internet import reactor, stdio, protocol, defer
from twisted.python import failure, reflect, log
from twisted.protocols import basic
from twisted.application import internet
from twisted.conch.insults import insults
from twisted.conch.manhole import ColoredManhole
# from twisted.conch.stdio import ConsoleManhole
from twisted.application import service
from twisted.conch import recvline
class CLIProtocol ( recvline.HistoricRecvLine ):
service = None
def connectionMade ( self ):
recvline.HistoricRecvLine.connectionMade ( self )
self.keyHandlers [ '\x01' ] = self.handle_HOME
self.keyHandlers [ '\x03' ] = self.handle_QUIT
self.keyHandlers [ '\x1a' ] = self.handle_QUIT
def connectionLost ( self, reason ):
log.msg ( "Connection Lost" )
def handle_QUIT ( self ):
self.terminal.loseConnection()
def lineReceived ( self, line ):
self.terminal.write ( '+++' + line )
class CLIServerProtocol ( insults.ServerProtocol ):
protocolFactory = CLIProtocol
class CLIService ( service.Service ):
def startService ( self ):
fd = sys.__stdin__.fileno()
oldSettings = termios.tcgetattr ( fd )
tty.setraw ( fd )
try:
p = CLIServerProtocol()
stdio.StandardIO ( p )
# reactor.run()
finally:
termios.tcsetattr ( fd, termios.TCSANOW, oldSettings )
os.write ( fd, "\r\x1bc\r" )
return service.Service.startService ( self )
######################################################################
# Create the application service hierarchy.
######################################################################
application = service.Application ( 'cliapp' )
cs = CLIService()
cs.setServiceParent ( application )
More information about the Twisted-Python
mailing list