pythonpython-2.7subprocesspython-multithreadingdns-sd

Python, running a subprocess and read output and not hanging the program


First post here and I must say I'm kinda amateur with programming in general... I'm using python 2.7.5 subprocess module on osx to read to output of an utility called dns-sd.

The goal is to find out what SSH file servers are running on my network. Using "dns-sd -B _ssh._tcp ." works fine in the following use:

from sys import *
from subprocess import *

class ProcessNAS(object):
    def __init__ (self, name):
        self.name = name
        self.status = False
        self.process = None

    def StartCheck(self):
        print "Checking for NAS..."
        stdout.flush()
        self.process = Popen( ["dns-sd", "-B", "_ssh._tcp", "."], stdout=PIPE )

        while True:
            line = self.process.stdout.readline()[:-1]
            print line
            if "Add" in line and self.name in line:
                self.status = True
                print "NAS '" + self.name + "' is available."
            elif "Rmv" in line and self.name in line:
                self.status = False
                print "NAS '" + self.name + "' is unavailable."


newCheckNAS = ProcessNAS("Drobo-FS")
newCheckNAS.StartCheck()

Note that this is a "live" utility and if the Python script keep running, new lines that dns-sd outputs will automatically be display by print. Here is a typical output:

Checking for NAS...
Browsing for _ssh._tcp
DATE: ---Tue 02 Jul 2013---
19:44:30.670  ...STARTING...
Timestamp     A/R Flags if Domain       Service Type       Instance Name
19:48:07.061  Add     2  4 local.       _ssh._tcp.         Drobo-FS
NAS 'Drobo-FS' is available.

The problem I have is that as soon as I do newCheckNAS.StartCheck(), the rest of the program just wait for the dns-sd utility to complete to continue. But this live utility will never stop, it need to continue monitoring in the background.

I looked at various threading, multiprocessing or even pybonjour modules but I don't really understand how they work... I guess I have to start a thread that runs this utility and another thread to listen to it?


Solution

  • Here is a very basic implementation of your class running as a new process. This will allow you to continue execution within your main script/process but you will need to use some sort of interprocess communication (ie. a shared memory data structure described in multiprocessing docs) if you want to do anything with the output of the child process within the parent.

    from sys import *
    from subprocess import *
    import multiprocessing
    
    class ProcessNAS(multiprocessing.Process):
        def __init__ (self, name):
            self.name_ = name
            self.status = False
            self.process = None
            super(ProcessNAS, self).__init__(target=self.StartCheck)
    
        def StartCheck(self):
            print "Checking for NAS..."
            stdout.flush()
            self.process = Popen( ["dns-sd", "-B", "_ssh._tcp", "."], stdout=PIPE )
    
            while True:
                line = self.process.stdout.readline()[:-1]
                print line
                if "Add" in line and self.name_ in line:
                    self.status = True
                    print "NAS '" + self.name_ + "' is available."
                elif "Rmv" in line and self.name_ in line:
                    self.status = False
                    print "NAS '" + self.name_ + "' is unavailable."
    
    newCheckNAS = ProcessNAS("Drobo-FS")
    newCheckNAS.start()  # <-- instead of StartCheck(), call start()
    
    import time
    time.sleep(4)
    newCheckNAS.terminate()  # <-- now have two processes - able to kill the child