selectel / pyte

Simple VTXXX-compatible linux terminal emulator
http://pyte.readthedocs.org/
GNU Lesser General Public License v3.0
658 stars 102 forks source link

Can Pyte be used as dropin to communicate with another tool that sometimes sends terminal comamnd characters? #33

Closed erikbgithub closed 9 years ago

erikbgithub commented 9 years ago

Hi,

I've got the following situation and after reading the docs I'm not sure if or if not pyte could be of help. I'd appreciate to get your opinion about it.

The situation is that I use pexpect to communicate via ssh remotely to another computer. To implement the ssh connection pexpect interally uses a pty for the interaction. That design decision leads to the user (me) to receiving terminal command chars in some situations. But actually I don't care about the command characters at all. All I want is to receive the text that the corresponding command prints to stdout on the remote device. In the linked example above all I want is what ls is telling me (and sometimes its returncode is also interesting).

Now I wonder if it would be possible to plug something like pyte on top of the pexpect API with the intention to simply let pyte ignore all these characters and filter them out of my output streams. I've literally spend months on manual fitlering already but I get so many different characters by now, that I can't handle it by myself any more (see linked issue in pexpect for an example).

I'm really really curious about your opinion. Also if you can suggest some websites or books that help me become more skilled in acting like a terminal, that would be great. I bet a real solution that is not a workaround can only be possible if I learn some more about terminals. But everything I've found was only part of the puzzle. It seems terminals do lots of things because of historical reasons that are not all documented in some place (or at least some place that can be found via google by the uneducated).

superbobry commented 9 years ago

Simply removing escape sequences from the program output might not work very well, because some of the escape sequences are required for correct rendering. For instance there's a zillion of escape sequences you need to handle before you get mc output rendered correctly. Try it yourself ;)

Anyway, it's fairly easy to ignore everything but the output events using pyte. All you need to do is to write a listener which only cares about the "draw" and "linefeed" events:

import io
import subprocess

import pyte

class Capture:
    """A listener which only handles rendering events."""
    def __init__(self, handle=None):
        self.handle = handle or io.StringIO()

    def draw(self, ch, **flags):
        self.handle.write(ch)

    def linefeed(self):
        self.handle.write("\n")

    # Probably the listener should also handle tabulation,
    # see `pyte.Screen` for method signature.

stream = pyte.Stream()
capture = Capture()
stream.attach(capture, only=["draw", "linefeed"])

# ^^^ now `stream` will dispatch events to `capture`. Events
# are atomic terminal actions, like linefeed, draw character,
# move cursor to specific position, etc.

output = subprocess.check_output("ls /tmp".split()).decode()
stream.feed(output)  # `feed` only works with strings.

# Make sure we didn't miss anything.
print(capture.handle.getvalue() == output)

Also if you can suggest some websites or books that help me become more skilled in acting like a terminal, that would be great.

Unfortunately, I'm not aware of any good sources aside from the terminal reference manuals. See pyte API docs for references.