[Twisted-Python] reactor.spawnProcess() - differences between Python & Bash deaths
Brendan Boerner
bboerner at rgmadvisors.com
Mon Oct 1 11:12:28 MDT 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