Using twisted.internet.process

  1. Introduction
  2. The ProcessProtocol
  3. Things that happen to your ProcessProtocol
  4. Things you can do from your ProcessProtocol
  5. An Example

Introduction

twisted.internet.reactor.spawnProcess is a twisted function that makes it possible to create and control spawned child processes from your Twisted-based application. Pipes are created to the child process, and added to the reactor core so that the application continues to not block while sending data into or pulling data out of the new process.

from twisted.internet import reactor

mypp = MyProcessProtocol()
reactor.spawnProcess(mypp, program, argv=[program, arg1, arg2],
                     env={'HOME': os.environ['HOME']})

To use this, you'll need to assemble a few things. The first is the exact specification of how the process should be run. This means a string that will be the program itself, a list that will be the argv array for the process, and a dict which provides the environment. Both the argv and env arguments are optional, but the default values are empty, and many programs depend upon both to be set correctly for them to function properly. At the very least, argv[0] should probably be the same as program. If you just provide os.environ for env, the child program will inherit the environment from the current program, which is usually the civilized thing to do (unless you want to explicitly clean the environment as a security precaution).

You can optionally set a path, in which case the child will switch to the given directory before starting the program. You can also set uid/gid, but only if you started as root.

The ProcessProtocol

The second thing you'll need to use spawnProtocolis an instance of ProcessProtocol (or, more likely, a subclass you have written) that is used to control the data going into and out of the process. This object behaves very much like a normal Protocol, in that you should write a subclass that overrides certain methods. These methods are called whenever some data from the process is available.

Things that can happend to your ProcessProtocol

These are the methods that you can usefully override in your subclass of ProcessProtocol:

The base-class definitions of these functions are all no-ops. This will result in all stdout and stderr being thrown away. Note that it is important for data you don't care about to be thrown away: if the pipe were not read, the child process would eventually block as it tried to write to a full pipe.

Things you can do from your ProcessProtocol

The following are the basic ways to control the child process:

Example

Here is an example that is rather verbose about exactly when all the methods are called. It writes a number of lines into the wc program and then parses the output.

#! /usr/bin/python

from twisted.internet import protocol
from twisted.internet import reactor
import re

class MyPP(protocol.ProcessProtocol):
    def __init__(self, verses):
        self.verses = verses
        self.data = ""
    def connectionMade(self):
        print "connectionMade!"
        for i in range(self.verses):
            self.transport.write("Aleph-null bottles of beer on the wall,\n" +
                                 "Aleph-null bottles of beer,\n" +
                                 "Take on down and pass it around,\n" +
                                 "Aleph-null bottles of beer on the wall.\n")
            self.transport.closeStdin() # tell them we're done
    def outReceived(self, data):
        print "outReceived! with %d bytes!" % len(data)
        self.data = self.data + data
    def errReceived(self, data):
        print "errReceived! with %d bytes!" % len(data)
    def inConnectionLost(self):
        print "inConnectionLost! stdin is closed! (we probably did it)"
    def outConnectionLost(self):
        print "outConnectionLost! The child closed their stdout!"
        # now is the time to examine what they wrote
        #print "I saw them write:", self.data
        (dummy, lines, words, chars, file) = re.split(r'\s+', self.data)
        print "I saw %s lines" % lines
    def errConnectionLost(self):
        print "errConnectionLost! The child closed their stderr."
    def processEnded(self, status_object):
        print "processEnded, status %d" % status_object.value.exitCode
        print "quitting"
        reactor.stop()
        
pp = MyPP(10)
reactor.spawnProcess(pp, "wc", ["wc"], {})
reactor.run()
process.py - process.py

The exact output of this program depends upon the relative timing of some un-synchronized events. In particular, the program may observe the child process close its stderr pipe before or after it reads data from the stdout pipe. One possible transcript would look like this:

% ./process.py 
connectionMade!
inConnectionLost! stdin is closed! (we probably did it)
errConnectionLost! The child closed their stderr.
outReceived! with 24 bytes!
outConnectionLost! The child closed their stdout!
I saw 40 lines
processEnded, status 0
quitting
Main loop terminated.
% 

Brian Warner <warner@lothar.com>
Last modified: Thu Oct 3 02:14:56 PDT 2002

Go Home