[Twisted-Python] reactor.spawnProcess() - differences between Python & Bash deaths
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
participants (1)
-
Brendan Boerner