python / subprocess / winch

python / subprocess / winch

  • Written by
    Walter Doekes
  • Published on

While I was writing a Python tool to wrap C Gdb so I could fetch some info out of it automatically, I ran into the issue that it reads the terminal size (lines x columns) to adjust its output.

I wanted consistent machine readable output, so I enlarged the terminal size programmatically: now row based output would not get wrapped by Gdb.

Later I noticed that it would cease to use the terminal size — in fact, use the default 80 columns — if I also redirected stderr to a non-tty. That left me with the terminal resize code which could be dropped again.

Here, for my own reference, Python terminal resize code. This simple python snippet starts an application of your choice from a 80x25 window.

import array, fcntl, termios, subprocess, sys

class WinchPopen(subprocess.Popen):
    def __init__(self, lines, columns, *args, **kwargs):
        self._winch_push(lines, columns)
        super(WinchPopen, self).__init__(*args, **kwargs)

    def wait(self):
        super(WinchPopen, self).wait()
        self._winch_pop()

    def _winch_push(self, lines, columns):
        fileno = sys.stdout.fileno()
        # Store window size.
        self.__stored_winsize = array.array('h', [0, 0, 0, 0])
        fcntl.ioctl(fileno, termios.TIOCGWINSZ, self.__stored_winsize, True)
        # Mangle window size.
        buf = array.array('h', [lines, columns, 0, 0])  # struct winsize
        for fileno in (sys.stdin.fileno(), sys.stdout.fileno(),
                       sys.stderr.fileno()):
            fcntl.ioctl(fileno, termios.TIOCSWINSZ, buf)

    def _winch_pop(self):
        for fileno in (sys.stdin.fileno(), sys.stdout.fileno(),
                       sys.stderr.fileno()):
            fcntl.ioctl(fileno, termios.TIOCSWINSZ,
                        self.__stored_winsize)

if __name__ == '__main__':
    proc = WinchPopen(columns=80, lines=25, args=sys.argv[1:])
    proc.wait()  # don't forget to call this!

Example calls:

$ ls /dev
autofs           cuse      hidraw0  loop3         network_latency     ram1   ram6    sda5      sr0     tty14  tty24  tty34  tty44  tty54  tty7       ttyS15  ttyS25  ttyS7      vcs4   vcsa7
block            disk      hidraw1  loop4         network_throughput  ram10  ram7    sdb       stderr  tty15  tty25  tty35  tty45  tty55  tty8       ttyS16  ttyS26  ttyS8      vcs5   vga_arbiter
bsg              dm-0      hidraw2  loop5         null                ram11  ram8    sdb1      stdin   tty16  tty26  tty36  tty46  tty56  tty9       ttyS17  ttyS27  ttyS9      vcs6   vhci
...
$ python smallwindow.py ls /dev
autofs     loop6             rtc       tty22  tty55  ttyS29
block      loop7             rtc0      tty23  tty56  ttyS3
bsg        loop-control      sda       tty24  tty57  ttyS30
...
$ python smallwindow.py vim 80x25.txt
...

Back to overview Newer post: flake8 / vim / python2 / python3 Older post: debian / packaging asterisk 13