Wait for a process to end that is created by a process called

2.9k views Asked by At

I'm writing a program in python which calls another program via the subprocess.Popen command.

The program being called launches a GUI that the user interacts with, but before that it goes through some services to authenticate the user.

With that being said, I'm trying to figure out a way to know when the user exits out of that GUI. Basically, I want to wait until the GUI is exited, and then continue in my program.

My problem is that since the program I make a call to goes through those services before the GUI is actually launched, it seems like the program I make a call to ends and then spawns a new process which is the GUI, and then 'm not able to wait on the pid because the pid I have as already terminated.

I have tried this:

p = subprocess.Popen('/user/sbin/startprogramtolaunchgui')
p.wait()
printf 'done'

But 'done' gets printed right away, not after the GUI is exited out of. Also, when I run the command

ps -ef

The program 'startprogramtolaunchgui' I called is not in the process list. BUT, I see the gui that it launched in the process list (the one that I want to monitor)

EDIT:

I came up with this:

def isRunning(pid):
      try: 
         os.kill(pid, 0)
         return True
      except OSError:
         return False


 p = subprocess.Popen('/user/sbin/startprogramtolaunchgui')
 time.sleep(5)
 temp = subprocess.check_output(['pgrep', 'gui']) #get pid of GUI process

 while(isRunning(int(temp))):
      pass

print 'GUI CLOSED'

It works...but is this really an okay way to do it??

2

There are 2 answers

1
jez On

This is a partial/tentative answer. Investigate the psutil library, which has tools for finding out the child processes of a given process, and for waiting for processes to finish. Here is a sketch of an approach. It has problems, but hopefully it gives you a starting point to work from:

import psutil
parent = psutil.Process(p.pid)
procs = set([parent])
while parent.is_running():  # hmm, OK, but what if one of the children spawns a grandchild *after* the parent has finished... 
    for child in parent.children(recursive=True):
        procs.add(child)
    # Hmm, here we are busy-waiting

psutil.wait_procs(procs)

Here is another approach that may work around the grandchild-of-dead-grandparent problem:

import psutil
procs = {psutil.Process(p.pid)}
while procs:
    procs = {proc for proc in procs if proc.is_running()}
    for proc in list(procs):
        for child in proc.children():
            procs.add(child)
    # Hmm, here we are busy-waiting

But you may still have the "reparenting" problem pointed out in the comments. The above approaches may work, or you may be better off simply calling ps repeatedly from Python and parsing its text output, waiting for whatever signature your GUI process has there to appear and then disappear.

6
Carles Mitjans On

If you use p.wait() you should also include stdout=PIPE as an argument to Popen call.

An alternative is to use p.communicate() which automatically calls wait() for you.

Either way should work.