pythonpython-2.7multiprocessingpexpectqnx

How to run pexpect on waited process using multiprocessing?


I am running Python 2.7 on a QNX system and I'm running into an issue where pexpect throws the following error:

ExceptionPexpect: isalive() encountered condition where "terminated" is 0, but there was no child process. Did someone else call waitpid() on our process?

The circumstances for this error are as follows: I have two files, pexpectTest.py and testPexpectError.py.

pexpectTest.py

    import multiprocessing
    import pexpect
    import sys
    
    pexp = pexpect.spawn('python testPexpectError.py')
    pexp.delaybeforesend = False
    pexp.logfile = sys.stdout

    def test():
        pexp.sendline('line')
        pexp.expect('>', timeout=None)
        pexp.close()
    
    mp = multiprocessing.Process(target=test)
    mp.start()
    mp.join()

testPexpectError.py

    import time
    
    while 1:
        input = raw_input(">")
        print input
        
        if input == 'exit':
           break
        
        time.sleep(1)

When called from a multiprocessing.Process, the exception at the top of this post gets thrown. When called from the main thread, the exception does not get thrown.

My main questions are:

  1. What is causing pexpect to react to the wait call in testPexpectError.py when expect is called from a multiprocessing.Process vs when expect is called in the main thread (if this even is the issue)?

  2. Is there any way around this?


Solution

  • Remember, the key distinguishing feature of multiprocessing is that it spawns a function inside a separate process.

    File descriptors are not shared across process boundaries (a child may get a copy of the parent's FDs, but this is an optional per-FD flag -- see https://docs.python.org/3/library/os.html#fd-inheritance describing the interface Python puts on top of that layer).

    Likewise, parent/child relationships only apply to the original parent: You can use waitpid() to reap a child process, but not a sibling (a fellow child of the same parent, which is the relationship your copy of python testPexpectError.py and the multiprocessing-spawned subprocess have here).

    If you really need to use multiprocessing here, move the spawn() into your test() function, so the child process is having its output and exit status read by its parent (rather than by a sibling), and everything will work.