0

I'm using Parmiko to invoke an ssh shell and I would like to direct all of the input/output to a Text widget. I found this solution:

class IORedirector(object):
    '''A general class for redirecting I/O to this Text widget.'''
    def __init__(self,text_area):
        self.text_area = text_area

class StdoutRedirector(IORedirector):
    '''A class for redirecting stdout to this Text widget.'''
    def write(self,str):
        self.text_area.write(str,False)

# In your Tkinter Widget
sys.stdout = StdoutRedirector( self )

The problem is I'm not familiar with classes enough to make this work (no matter how simple it may be) and time is unfortunately not on my side. That being said, what is the easiest way to do this without a class definition? Could that class be converted to a general function and call it to do the redirect?

Thanks for any help.

Edit: Upon further experimenting, I altered

self.text_area.write(str,False)

to

self.text_area.insert(END,str)

This now works as print statements now appear in the text box. Now my issue is when I call interactive.interactive_shell(chan), the program crashes with

AttributeError: 'StdoutRedirector' object has no attribute 'flush'

Any ideas with this?

Edit2: I shall keep digging. Now have found this. I've added a no-op for flush and consequently get:

AttributeError: 'NoneType' object has no attribute 'settimeout'

I feel like this will be a gopher hole. Any ideas from now?

Edit 3: I will chase this to the end. Needed a (self) parameter added like def flush(self).

I'm not sure where this is going anymore so back to a simple solution of routing a Paramiko interactive shell to Text box with input and output would be great.

Community
  • 1
  • 1
user3000724
  • 651
  • 3
  • 11
  • 22
  • The flush error would have been easy to explain and solve, as it is a standard io method (see my answer). Explaining the `settimeout` exception requires the full traceback. That attribute is not a standard part of io streams and the exception might not be related to your stdout class. – Terry Jan Reedy Feb 19 '16 at 22:46
  • Does Paramiko normally run in a terminal and use stdin/out/err for i/o? – Terry Jan Reedy Feb 19 '16 at 22:49
  • I recommend deleting 'without classes' from title and text as it is not possible and its presence can only discourage answers other answers. – Terry Jan Reedy Feb 19 '16 at 22:51

1 Answers1

0

Python is a language for manipulating class instances with methods. Some of this is hidden. For instance, type(a).__add__(a,b) and a.__add__(b) are disguides as a + b. However, you cannot get far beyond the basics without understanding classes, instances, and instance methods. And sending print output to a tk text widget is, as you discovered, beyond the basics.

As you also discovered, the exact requirements of a sys.stdout replacement depend on the code that uses it. In the absence of paramiko giving you a detailed spec for what it actually requires of sys.stdout, you can either program a complete replacement or program a partial replacement as you get exceptions.

For example, IDLE has the following code, currently in idlelib.PyShell.

class PseudoFile(io.TextIOBase):

    def __init__(self, shell, tags, encoding=None):
        self.shell = shell
        self.tags = tags
        self._encoding = encoding

    @property
    def encoding(self):
        return self._encoding

    @property
    def name(self):
        return '<%s>' % self.tags

    def isatty(self):
        return True


class PseudoOutputFile(PseudoFile):

    def writable(self):
        return True

    def write(self, s):
        if self.closed:
            raise ValueError("write to closed file")
        if type(s) is not str:
            if not isinstance(s, str):
                raise TypeError('must be str, not ' + type(s).__name__)
            # See issue #19481
            s = str.__str__(s)
        return self.shell.write(s, self.tags)

The shell.write method has a text.insert('insert', s, tags) call. There is no flush call because the IDLE code that writes to stdout knows that write inserts text immediately and that no flush is needed. Other unused and unneeded attributes and methods are also missing.

The specifications for 3.x i/o streams are in the io module doc. You should look at classes IOBase and TextIOBase (and possibly TextIOWrapper). For instance, the IOBase flush entry says "Flush the write buffers of the stream if applicable. This does nothing for read-only and non-blocking streams." Text widget insertion is non-blocking, and no return is needed, so def flush(self): pass should be sufficient.

Terry Jan Reedy
  • 18,414
  • 3
  • 40
  • 52
  • Thanks for the thorough response. I spent the weekend reviewing classes and now feel decently comfortable with them (enough to convert my ~500 lines to using multiple classes than a bunch of global variables and plain functions. For some reason though, the application crashes when I open the SSH interactive shell. It uses this for the shell: if chan in r: try: x = u(chan.recv(1024)) if len(x) == 0: break sys.stdout.write(x) sys.stdout.flush() I'm not sure why the sys.stdout line (What I think is it) would crash it – user3000724 Feb 22 '16 at 16:19