Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Pexpect intermittently hanging (not detecting EOF) after executing certain commands?

Context:

I have some code written using pexpect, whose job is to give "live" output of a command. I.e. print something out when a command generates some output, or soon after, rather than waiting until the command completes and then interacting with its output.

All I'm doing is starting and stopping a service. I do this by spawning the process, and then outputting each line as it is printed, like so:

def watch(process):
    output = ""
    while True:
        try:
            line = process.read_nonblocking(timeout = -1)
            print(line, end ="")
            output += line
        except pexpect.EOF:
            break
    del process
    return output

while True:
    print("IN 1")
    process = pexpect.spawn("service",["zend-server", "stop"], timeout = None)
    watch(process)
    print("OUT 1")

    print("IN 2")
    process = pexpect.spawn("service",["zend-server", "start"], timeout = None)
    watch(process)
    print("OUT 2")

This code should just loop the service: start it and stop it over and over, printing the output of the start/stop as it goes. It prints the output fine. However, it eventually hangs right before "OUT 2". I can view the output, and see the service call stop its execution. The watch function never raises an EOF and exits, however.

This doesn't happen with every service. Some services loop indefinitely. zend-server, however, along with a few other unrelated commands, fail intermittently in the same way.

By "eventually hangs", I mean that it starts/stops the service a few (variable on each run) times, and hangs. It usually gums up after 4-6, though never on the first call--always at least on the second (hence the del statement; I figured I'd play it safe).

Python 2.6.6, CentOS (64) 6.3, Pexpect 2.3-6, FWIW

Question:

Why is pexpect hanging on certain commands? How should I resolve this issue? Using timeouts isn't a feasible solution, as some of these commands really can run for an arbitrarily long time. service zend-server stop is just the one I picked for an example because it doesn't take that long, and I can observe it finishing.

What I've Tried:

I've tried replacing the watch method with the following, which uses expect('\n'), but the results are the same: a variable number of restarts, and then an eventual hang.

I can also add pexpect.EOF into the array that is expected along with \n, and handle the return value to break out of the loop, it still hangs in the same place.

def watch2(process):
    output = ""
    done = False
    while True:
        try:
            if not process.isalive():
                line = process.readline()
                done = True
            else:
                process.expect(['\n'])
                line = process.before
             print(line)
            output += line
            if done:
                raise pexpect.EOF(0)
        except pexpect.EOF:
            break
    del process
    return output
like image 860
Zac B Avatar asked Sep 28 '12 20:09

Zac B


1 Answers

Looks like a buffering problem where pexpect is waiting for more data. You could try disabling buffering by passing maxread=1 to pexpect.spawn()

like image 93
barracel Avatar answered Nov 05 '22 09:11

barracel