0

I have a program that constantly pipes status information to a python script through stdin. Those are separated by a new line, so one update is one new line in stdin.

In my Python script I have an endless loop that waits for new input from stdin and then does some post processing. I want this post-processing to happen as soon as new information is available from stdin but at the least after 5 seconds.

Currently I have:

line = ""
while True:
    if sys.stdin in select([sys.stdin], [], [], 5)[0]:
        line = sys.stdin.readline().strip()
        do_something(line) # do this only if something can be read from stdin

    # do this in any case every 5 secs
    # (but does rely on the most recent info from stdin)
    do_more(line)

This works great if one loop iteration finishes before a new update is available from stdin.

However, if a new line is written to stdin while the script is processing the previous update select blocks for 5 seconds instead of returning immediately. Additionally, in case of an update between loop iterations select without a timeout returns false:

sys.stdin in select([sys.stdin], [], [], 0)[0]

I tried to change the condition this way:

if not sys.stdin.isatty() or sys.stdin in select([sys.stdin], [], [], 5)[0]:

This works for detecting changes between loop iterations but sys.stdin.isatty() blocks if no new line is available, so select is never evaluated.

What I actually need is a function that tests if the stdin buffer is empty or not in a non-blocking way. Is there such a function? Or any other way to achieve what I want? I'm only interested in Linux so portability is not an issue.

2dpc
  • 691
  • 5
  • 11

1 Answers1

1

Buffered I/O (e.g. sys.stdin.readline()) and select.select don't work together, because buffered I/O is reading too much without returning it all. Use os.read(0, ...) instead sys.stdin.readline() to read bytes after select.select has returned, and do your own input buffering based on that.

pts
  • 80,836
  • 20
  • 110
  • 183