Is shutil.get_terminal_size useless?

eryk sun eryksun at
Sat Jan 28 07:09:53 EST 2017

On Sat, Jan 28, 2017 at 8:03 AM, Steve D'Aprano
<steve+python at> wrote:
> print('shutil:', shutil.get_terminal_size(fallback=(999, 999)))
> print('os:', os.get_terminal_size(0))
> But if I pipe the output to something else, the shutil version fails to
> determine the correct terminal size, and falls back on the default:

The high-level shutil wrapper uses sys.__stdout__, without falling
back on sys.__stdin__ or sys.__stderr__, so it has to rely on the
environment variables and/or fallback value when stdout isn't a
terminal. OTOH, in this case you're calling os.get_terminal_size(0) on
the STDIN file descriptor, which obviously works -- at least on Unix
-- because it's a tty. On Windows that generally won't work because
getting the screen size requires a handle for a screen buffer, not an
input buffer. In this case, os.get_terminal_size(2) would generally
work on Windows because stderr hasn't been redirected.

I just wrote an extended version of shutil.get_terminal_size that
tries six (minus two) ways to Sunday to get the terminal size,
starting with /dev/tty on Unix and CONOUT$ on Windows. It's needlessly
complicated by the Windows implementation of os.get_terminal_size,
which is strangely hard coded. os.get_terminal_size calls GetStdHandle
with a fake mapping of 0, 1, and 2 to the standard handles. It should
just call get_osfhandle on the given file descriptor. That way someone
could use something like the following:

    with open('CONOUT$', 'r+') as conout:
        size = os.get_terminal_size(conout.fileno())

which would be guaranteed to work if the process is attached to a
console. The current implementation forces one to temporarily modify a
standard handle, which has to be gated by a lock for thread safety.

More information about the Python-list mailing list