On Jun 7, 7:30 am, Ian Epperson <
i...@epperson.com> wrote:
> I experimented more with this solution and found that it's not bullet
> proof. If the user enters some data then EOF (ctrl-D on OSX, ctrl-Z on
> Windows) the entire program halts on the unsatisfied readline() until the
> user presses return. I may hack on it a bit tonight to come up with
> something better.
>
> Ian E.
Thank you. That looked promising -- too bad EOF didn't behave. I
ended up coding a conceptually similar, but lower-level solution that
works with EOF; this one is generalized to work with file-descriptors.
I would use it like this for stdin:
```python
reader = GLineReader(sys.stdin.fileno(), makeNonBlocking=False)
line = reader.readline()
```
And here is GLineReader:
```python
import errno
import fcntl
import os
import socket
from gevent.select import select as gevent_select
class GLineReader(object):
""" Read a line of text from a file descriptor up to and including
the very
first '\n', using gevent-friendly API.
NOTE: We implement this as a class in order to faclitate performance
improvements, such as buffered reads.
TODO: next release of gevent v1.0 should have a FileObject class
that might
do what we're trying to accomplish here.
"""
def __init__(self, fd, makeNonBlocking=True):
""" Construct the reader; configure the given file descriptor for
non-blocking operation
WARNING: if the fd is from sys.stdin.fileno(),
then changing this fd to non-blocking also has
the side-
effect of making the file desriptor in sys.stdout
non-blocking, so print and sys.stdout.write/flush
will
sometimes fail with "IOError: [Errno 35] Resource
temporarily unavailable" (35=errno.EWOULDBLOCK).
"""
if makeNonBlocking:
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
self._fd = fd
self._blocking = not makeNonBlocking
return
def readline(self):
""" Read a line of text from a file descriptor up to and including
the very
first '\n', using gevent-friendly API.
NOTE: Lines are assumed to be terminated with '\n'
retval: The read string, including the terminating '\n',
unless EOF is
encountered first, in which case the string read so
far will be
returned without the terminating '\n'; empty string
is returned
on EOF if no line characters preceded it.
"""
fd = self._fd
data = ""
rlist = [fd]
wlist = xlist = []
while not data.endswith('\n'):
# Wait for readability
rready = gevent_select(rlist, wlist, xlist)[0]
assert fd in rready
# Read
newData = ""
while newData != '\n':
try:
newData = os.read(fd, 1)
except socket.error as e:
if e.args[0] == errno.EWOULDBLOCK:
break
else:
raise
else:
if not newData:
# EOF reached
return data
data += newData
if self._blocking:
# Don't want to risk blocking on next char if the fd is
blocking
break
return data
```