[Twisted-Python] reactor.spawnProcess() - differences between Python & Bash deaths

Brendan Boerner bboerner at rgmadvisors.com
Mon Oct 1 13:12:28 EDT 2007


Hi,

I'm seeing (or not seeing :-) ) behavior which I cannot explain.

Attached are two .py scripts and a bash script -

echo.bash - echo a message
echo.py - echo a message
py_spawn.py - launch either echo.bash or echo.py using
reactor.spawnProcess().

If I have py_spawn.py launch echo.bash and then kill the '/bin/bash
./echo.bash' process using a SIGTERM py_spawn reports:

error: errorMessage: A process has ended with a probable error  
condition: process ended by signal 15.
error: exitCode: None
error: signal: 15
error: status: 15
processEnded, status <twisted.python.failure.Failure <class  
'twisted.internet.error.ProcessTerminated'>>

Which is what I expect.

If instead I launch echo.py and then kill the
'/opt/local/bin/python2.5 ./echo.py -m message' process py_spawn
reports:

error: exitCode: 0
error: signal: None
error: status: 0
processEnded, status <twisted.python.failure.Failure <class  
'twisted.internet.error.ProcessDone'>>

which is not what I expect.  In particular I am 99% certain that this
is relatively recent behaviour which showed up only last week - prior
to that I was seeing that if I killed a spawned .py process that I'd
see that it was terminated with a signal 15 ala the bash case.

I've tested this on Mac and Debian boxes with identical results.

Any suggestions or insights are appreciated.

Regards,
Brendan

--- py_spawn.py ---
#! /opt/local/bin/python2.5

from twisted.internet import error, reactor, defer, protocol

import getopt
import pickle
import pprint
import subprocess
import signal
import sys

Dumper = pprint.pformat

class MyProcessProtocol(protocol.ProcessProtocol):

     def __init__(self):
         self.outdata = ""
         self.errdata = ""
         pass

     def connectionMade(self):
         print "connectionMade!"

     def outReceived(self, data):
         print "outReceived! with %d bytes!" % len(data)
         self.outdata = self.outdata + data

     def errReceived(self, data):
         print "errReceived! with %d bytes!" % len(data)
         self.errdata = self.errdata + data

     def inConnectionLost(self):
         print "inConnectionLost! stdin is closed! (we probably did it)"

     def outConnectionLost(self):
         print "outConnectionLost! The child closed their stdout!"
         print "I saw them write:\n", self.outdata

     def errConnectionLost(self):
         print "errConnectionLost! The child closed their stderr."
         print "I saw them write:\n", self.errdata

     def processEnded(self, status):
         print "debug: type(status): %s" % type(status.value)
         print "debug: isinstance(status.value, error.ProcessDone): % 
s" % \
             isinstance(status.value, error.ProcessDone)
         print "debug: isinstance(status.value,  
error.ProcessTerminated): %s" % \
             isinstance(status.value, error.ProcessTerminated)

         if isinstance(status.value, error.ProcessDone):
             print "error: exitCode: %s" % status.value.exitCode
             print "error: signal: %s" % status.value.signal
             print "error: status: %s" % status.value.status

         elif isinstance(status.value, error.ProcessTerminated):
             print "error: errorMessage: %s" % status.getErrorMessage()
             print "error: exitCode: %s" % status.value.exitCode
             print "error: signal: %s" % status.value.signal
             print "error: status: %s" % status.value.status

         print "processEnded, status %s" % repr(status)
         print "quitting"
         reactor.stop()

def usage():
     print "Usage: py_spawn.py"

def main():

     processProtocol = MyProcessProtocol()

     # killing the bash process using SIGTERM results in:
     # error: errorMessage: A process has ended with a probable error  
condition: process ended by signal 15.
     # error: exitCode: None
     # error: signal: 15
     # error: status: 15
     # processEnded, status <twisted.python.failure.Failure <class  
'twisted.internet.error.ProcessTerminated'>>

     reactor.spawnProcess(processProtocol, 'bash', ["bash", "-c",  
"echo.bash"], env=None)

     # killing the bash process using SIGTERM results in:
     # error: exitCode: 0
     # error: signal: None
     # error: status: 0
     # processEnded, status <twisted.python.failure.Failure <class  
'twisted.internet.error.ProcessDone'>>

     #reactor.spawnProcess(processProtocol, 'bash', ["bash", "-c", "./ 
echo.py -m message"], env=None)

     reactor.run()

if __name__ == "__main__":
     main()


--- echo.py ---
#!/opt/local/bin/python2.5


from optparse import OptionParser, OptParseError

from twisted.internet import reactor
from twisted.spread import pb
from zope.interface import implements

import pprint
import sys

Dumper = pprint.pformat

def run(duration, message):

     duration -= 1
     print "duration: %d, message: %s" % (duration, message)
     if duration != 0:
         reactor.callLater(1, run, duration, message)
     else:
         reactor.stop()

def main():
     print "debug: args: %s" % sys.argv[1:]
     try:
         duration = 0
         message = "No message"

         parser = OptionParser()
         parser.add_option("-d", "--duration=", dest="duration",
             metavar="INT",
             default=duration,
             help="How long to run, 0 will run forever [default: % 
default]")
         parser.add_option("-m", "--message=", dest="message",
             default=message,
             help="Message to display [default: %default]")

         (opts, args) = parser.parse_args()
         duration = int(opts.duration)
         message = opts.message

         print "debug: opts: %s, args: %s" % (opts, args)

     except OptParseError, e:
         # print help information and exit:
         print "exception: %s" % e
         sys.exit(2)

     reactor.callLater(0, run, duration, message)
     reactor.run()

if __name__ == "__main__":
     main()


--- echo.bash ---
#!/bin/bash

while true; do
     echo -n "This is a message ";
     date;
     sleep 1;
done





More information about the Twisted-Python mailing list