Best way of finding terminal width/height?
Grant Edwards
grante at visi.com
Wed Feb 8 11:14:34 EST 2006
On 2006-02-08, Joel Hedlund <joel.hedlund at gmail.com> wrote:
>> sys.stdin.read() will return when ... the
>> underyling read() call is aborted by a signal.
>
> Not "return", really?
Yes, really. On a SIGWINCH, the read() will _either_ return
currently buffered data or thrown an IOError exception. You
need to handle either one.
> Won't it just pass an exception?
That seems to be the behavior if there is no buffered data to
return.
> I thought that was what I was catching with the "except
> IOError" part there? I assumed that sys.stdin.read() would
> only return a value properly at EOF?
That assumption seems to be wrong. sys.stdin.read() also
returns w/o an exception if there's buffered data and a
SIGWINCH occurs. Whether that is "proper" or not, I don't
know, but that's what it does.
Hmm, here's what the library reference says about file object's
read() method:
read([size])
Read at most size bytes from the file (less if the read
hits EOF before obtaining size bytes). If the size argument
is negative or omitted, read all data until EOF is reached.
The bytes are returned as a string object. An empty string
is returned when EOF is encountered immediately. (For
certain files, like ttys, it makes sense to continue
reading after an EOF is hit.) Note that this method may
call the underlying C function fread() more than once in an
effort to acquire as close to size bytes as possible. Also
note that when in non-blocking mode, less data than what
was requested may be returned, even if no size parameter
was given.
There appear to be a couple problems with this description:
1) It says that read() in blocking mode without a size
parameter it will read until EOF. This is not what happens
when reading a terminal that receives SIGWINCH, so you're
right: read() it isn't working as described.
2) It also says that it makes sense to continue to read a tty
after you get an EOF. That's not true. Once you get an
EOF on a tty, there's no point in reading it any more:
you'll continue to get an EOF forever.
> It looks to me as if sys.stderr.read() really gets an EOF at
> the final linebreak fed into the terminal prior to window size
> change, because the final unterminated line shows up on my
> shell prompt.
Python's read() (which seems to call fread()) didn't get an EOF
from fread(), it got an error from fread() because the C read()
call that was made by fread() returned an error because it was
interrupted by a signal.
In Python, EOF is when read() returns '' (the empty string).
> Like so:
>
> $ python winch.py
> moo moo
> cow cowmoo moo
> $ cow cow
>
> In this example I type moo moo[ENTER]cow cow on my keyboard
> and then resize the window.
>
> Now that EOF has to come from somewhere
There was no EOF. read() returned because of the signal, not
because of an EOF.
> (since there's no
> IOError or other exception, or the program wouldn't terminate
> nicely with nothing on stderr) and I'd like to point the blame
> at the terminal. Or is there something really fishy inside
> sys.stdin.read() or signal that puts EOFs into streams?
>
> But anyway, as long as this behavior only shows up on
> interactive operation, the user will likely spot it anyway and
> can react to it. So I think this code would be pretty safe to
> use. What do you think?
>
>> Resign the terminal will abort pending I/O operations on
>> that terminal. It won't terminal I/O operations pending on
>> other devices/files.
>
> What do you mean by "abort"? I can accept that "aborting" may
> lead to raising of IOError (which we can catch and retry), but
> not to arbitrary insertion of EOFs into streams (which we
> cannot distinguish from the real deal coming from the user).
It didn't insert an EOF, it just caused read() to return
"prematurely". You should call read() again until it receives
a _real_ EOF and returns ''. Take another look at my example:
#!/usr/bin/python
import signal, os, sys
_bTerminalSizeChanged = False
def report_terminal_size_change(signum, frame):
global _bTerminalSizeChanged
_bTerminalSizeChanged = True
signal.signal(signal.SIGWINCH, report_terminal_size_change)
while True:
try:
s = sys.stdin.read()
if not s:
sys.stderr.write("EOF\n")
break
sys.stdout.write(s)
except IOError:
sys.stderr.write("IOError\n")
if _bTerminalSizeChanged:
sys.stderr.write("SIGWINCH\n")
_bTerminalSizeChanged = False
--
Grant Edwards grante Yow! Yow! It's a hole
at all the way to downtown
visi.com Burbank!
More information about the Python-list
mailing list