2

I encountered a strange behavior when i was building a command line interface in python. Here is the striped down version of the code that can reproduce the issue.

from twisted.internet import reactor, stdio
from twisted.protocols import basic

class CommandLine(basic.LineReceiver):
    def __init__(self):
        self.linebuf = ''
        self.setLineMode()

    # why lineReceived  doesn't work?
    # def lineReceived(self, data):
    def dataReceived(self, data):
        print 'data received ' + ' '.join([str(ord(c)) for c in data ])
        print data


if __name__=='__main__':
    stdio.StandardIO(CommandLine())
    reactor.run()

The code above works as intended, out put in the form of "data received 108 115 115 10" is printed everytime a line is entered. Here is a sample output using dataReceived:

$ python cmdline.py
hello
data received 104 101 108 108 111 10
hello

^[[A
data received 27 91 65 10

However nothing gets printed out except the echo of the command line itself when I use lineReceived instead of dataReceived in the code above. Example output using lineReceived:

$ python cmdline.py
hello
^[[A

According the the documentation on lineReceived, the lineReceived function gets invoked when a line is received with the LineReceiver in line mode.

For now I am using dataReceived to make it work. But I would like to find out why lineReceived is not working as intended. Any hint, suggestions, advice would be very much appreciated!

Regards.

flintlock
  • 97
  • 7

1 Answers1

2

The reason is line delimiter constant which is set to r'\r\n' by default (MS Windows delimiter). Try to set it to '\n' (Linux and Mac OS) instead:

class CommandLine(basic.LineReceiver):
    delimiter = '\n'
Sergey Shubin
  • 3,040
  • 4
  • 24
  • 36
  • That's exactly why. Thank you so much! – flintlock Jan 11 '17 at 09:04
  • One small error in your code snippet thought...it should be "self.delimiter". – flintlock Jan 11 '17 at 09:06
  • @flintlock Yes, if you are using it in `__init__` method. But it is a class level [variable](https://github.com/twisted/twisted/blob/twisted-16.5.0/src/twisted/protocols/basic.py#L528), better to define it the same way. – Sergey Shubin Jan 11 '17 at 09:15
  • I see...you're right. BTW what is the difference between lineReceived instead of dataReceived then, except that when you use lineReceived you have the flexibility to redirect the data stream to rawDataReceived very easily? When I override dataReceived, it seems to be called only when a line is received. – flintlock Jan 11 '17 at 09:36
  • @flintlock It's a common idea of twisted protocols to use `dataReceived` to split all incoming data to atomic entities. For `LineReceiver` this entity is line and for `HTTP11ClientProtocol` for example it is HTTP packet. Usually you don't need to override this method unless you write a subclass from `protocol.Protocol` itself. As for `LineReceiver.dataReceived`, it mostly handles text buffering and splitting for you. – Sergey Shubin Jan 11 '17 at 10:27
  • @flintlock `LineReceiver.dataReceived` can also handle some specific cases like the lack of delimiter in the end of input which was your problem :) So better to keep it and receive data in `lineReceived`. – Sergey Shubin Jan 11 '17 at 10:39
  • @Sergey_Shubin Thank you so much! One last question, I realized I am still getting one line at a time, even in raw mode, so I am suspecting Twisted.stdio.StandardIO is buffering the line for me. But actually I want to get a callback for each key stroke because that way I can handle some special key press such as arrow keys. What would you recommend as a solution? Appreciated! – flintlock Jan 11 '17 at 11:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/132913/discussion-between-sergey-shubin-and-flintlock). – Sergey Shubin Jan 11 '17 at 13:07