views:

257

answers:

3

Hi,

I'm currently launching a programme using subprocess.Popen(cmd, shell=TRUE)

I'm fairly new to Python, but it 'feels' like there ought to be some api that lets me do something similar to:

subprocess.Popen(cmd, shell=TRUE,  postexec_fn=function_to_call_on_exit)

I am doing this so that function_to_call_on_exit can do something based on knowing that the cmd has exited (for example keeping count of the number of external processes currently running)

I assume that I could fairly trivially wrap subprocess in a class that combined threading with the Popen.wait() method, but as I've not done threading in Python yet and it seems like this might be common enough for an API to exist, I thought I'd try and find one first.

Thanks in advance :)

A: 

AFAIK there is no such API, at least not in subprocess module. You need to roll something on your own, possibly using threads.

pajton
A: 

multiprocessing?

msw
Could you specifically mention the name of the on-exit parameter that you are thinking of in the multiprocessing module?
Brandon Craig Rhodes
+2  A: 

You're right - there is no nice API for this. You're also right on your second point - it's trivially easy to design a function that does this for you using threading.

import threading
import subprocess

def popenAndCall(onExit, popenArgs):
    """
    Runs the given args in a subprocess.Popen, and then calls the function
    onExit when the subprocess completes.
    onExit is a callable object, and popenArgs is a list/tuple of args that 
    would give to subprocess.Popen.
    """
    def runInThread(onExit, popenArgs):
        proc = subprocess.Popen(*popenArgs)
        proc.wait()
        onExit()
        return
    thread = threading.Thread(target=runInThread, args=(onExit, popenArgs))
    thread.start()
    # returns immediately after the thread starts
    return thread

Even threading is pretty easy in Python, but note that if onExit() is computationally expensive, you'll want to put this in a separate process instead using multiprocessing (so that the GIL doesn't slow your program down). It's actually very simple - you can basically just replace all calls to threading.Thread with multiprocessing.Process since they follow (almost) the same API.

Daniel G
Thanks. This is what I was going to do.Unfortunately there's a problem that I can't replicate in a simple scenario but can in my actual programme :(If I use threading, not multiprocessing, proc.wait() doesn't return until I do something else with subprocess. If I use multiprocessing it works perfectly. However, using multiprocessing I have to fuss with shared memory. I've done that now, but I'm not sure I'm happy with the overhead. Any ideas why subprocess might behave differently in a thread to a process (changing which one I use and nothing else causes/solves the issue)?
Who
@Who I'm sorry - I don't know why threading wouldn't work, or why it would work any differently than multithreading in this case. That seems pretty strange. Is the overhead of shared memory a performance bottleneck, or is it just ugly?
Daniel G