bobber6467 / python-nose

Automatically exported from code.google.com/p/python-nose
0 stars 0 forks source link

ProcessProtocol broken when using nose with python 2.7.3, twisted 12.0.0 #482

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
Environment
===========
- debian wheezy64 (updated 30.01.2013)
- python 2.7.3~rc2-1
- twisted 12.0.0-1

Detailed defect description
===========================

I wrote two tests one using twisted.internet.reactor and one using 
nose.twistedtools.reactor.

With , when the child exits (whatever the reason), the function 
twisted.internet.Process.maybeCallProcessEnded is called on each connection 
closing (the last close calls twisted.internet._BaseProcess.reapProcess). In 
this function, "os.waitpid(self.pid, os.WNOHANG)" can seldom return (0, 0). In 
this case and later on, twisted calls an second time the function from 
reapAllProcesses.

With nose.twistedtools.reactor, the (0,0) is (quite) always returned and the 
second call using reapAllProcesses is never done, letting the child process in 
a zombie state!

NOTE: This problem has never occurred with debian squeeze, python 2.6.6, 
twisted 10.1.0.

TEST twisted.internet.reactor
=============================

import os

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

class MyProcessProtocol(protocol.ProcessProtocol):

    def childDataReceived(self, childFD, data):
        print "%d>" % (childFD, ), data

    #def childConnectionLost(self, childFD):
    #    print("childConnectionLost(%d)" % (childFD, ))

    def processExited(self, status):
        print("processExited(%s)" % (str(status), ))

    def processEnded(self, status):
        print("processEnded(%s)" % (str(status), ))
        reactor.stop() #@UndefinedVariable

def main():
    proto = MyProcessProtocol()
    #callargs = ["ls", "-l"]
    #callargs = ["ls", "-W"] # exit with 2
    callargs = ["echo", "Hello World"]
    process = reactor.spawnProcess(proto, callargs[0], callargs, os.environ) #@UndefinedVariable
    proto.process = process

if __name__ == "__main__":
    reactor.callWhenRunning(main) #@UndefinedVariable
    reactor.run() #@UndefinedVariable

TEST twisted.twistedtools.reactor
=================================

import os
import unittest

from nose.twistedtools import reactor
from nose.twistedtools import deferred

from twisted.internet import protocol
from twisted.internet import defer

class MyProcessProtocol(protocol.ProcessProtocol):
    def __init__(self, deferred):
        self._deferred = deferred
        self._timer = None

    def childDataReceived(self, childFD, data):
        print "%d>" % (childFD, ), data

    def childConnectionLost(self, childFD):
        print("childConnectionLost(%d)" % (childFD, ))
        # Here starts a workaround code to ensure that the process is reaped!
        # Only with debian wheezy, python 2.7.3, twisted 12.0.0 and nose 
        if self._timer is None:
            def reapProcess():
                print "reapProcess?"
                self._timer = None
                self.process.reapProcess()
                if self.process.pid:
                    self._timer = reactor.callLater(0.1, reapProcess)
            self._timer = reactor.callLater(0.1, reapProcess)

    def processExited(self, status):
        print("processExited(%s)" % (str(status), ))
        # Workaround
        if self._timer:
            self._timer.cancel()
            self._timer = None

    def processEnded(self, status):
        print("processEnded(%s)" % (str(status), ))
        # Try to let a chance to twisted to call reapAllProcesses - DON'T WORK
        #reactor.callLater(1, self._deferred.callback, None)
        self._deferred.callback(None)

class Test(unittest.TestCase):

    @deferred()
    def test_process1(self):
        deferred = defer.Deferred()
        proto = MyProcessProtocol(deferred)
        #callargs = ["ls", "-l"]
        callargs = ["ls", "-W"]
        process = reactor.spawnProcess(proto, callargs[0], callargs, os.environ) #@UndefinedVariable
        proto.process = process
        return deferred

if __name__ == "__main__":
    unittest.main()

Original issue reported on code.google.com by philippe...@gmail.com on 1 Feb 2013 at 1:47

GoogleCodeExporter commented 8 years ago
nose development has moved to github (https://github.com/nose-devs/nose). could 
you report this issue there? Thanks.

Original comment by jpelle...@gmail.com on 1 Feb 2013 at 3:24