using @defer.inlineCallbacks and yield correctly
I'm sending this email to separate out the two issues I'm having to gain a better understanding of how to properly code what I'm attempting to do. I'd like to use spawnProcess to spawn a process but with a timeout. If the timeout is reached I'd like to kill the process. You can see in the error section that I'm getting an error with defer and no yield. I was hoping someone could tell me what my code is supposed to do that it's not doing so that I can stop getting the error found at the bottom. from what I understand @defer.inlineCallbacks is used to defer a async callback function like how i'm using CallLater below, but where is the yield supposed to be? and how should the below code change? ======================== Code ======================== command = "xvfb-run --auto-servernum firefox -P %s" % profile_id logging.debug("command: %s", command) args = shlex.split(command) self.pp = TimedProcessProtocol(self, 100) subprocess = reactor.spawnProcess(self.pp, args[0], args, env = os.environ, usePTY=1) logging.debug("spawned a process: pid: %d", subprocess.pid) self.pid = subprocess.pid ======================== ProcessProtocol Class ======================== class TimedProcessProtocol(protocol.ProcessProtocol): def __init__(self, firefoxProcess, timeout): self.timeout = timeout self.firefoxProcess = firefoxProcess self.killed = None def isProcessDead(self): logging.debug("isProcessDead called") if self.killed is None: return False else: logging.debug("process is dead!!__________") return True def killProcessIfAlive(self): logging.debug("killProcessIfAlive called") #@defer.inlineCallbacks try: #yield self.transport.signalProcess('KILL') if self.killed is None: os.kill(-self.transport.pid, signal.SIGTERM) except error.ProcessExitedAlready: logging.debug("process already exited") pass def connectionMade(self): logging.debug("connection made timeout = %d", self.timeout) @defer.inlineCallbacks def onTimer(): logging.debug("timeout triggered") self.firefoxProcess.handleProcessTimedOut() self.killProcessIfAlive() d = reactor.callLater(self.timeout, onTimer) ======================== Errors ======================== 2011-08-31 12:30:22-0700 [-] Unhandled error in Deferred: 2011-08-31 12:30:22-0700 [-] Unhandled Error Traceback (most recent call last): File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 1170, in run self.mainLoop() File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 1179, in mainLoop self.runUntilCurrent() File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 778, in runUntilCurrent call.func(*call.args, **call.kw) File "/usr/lib/python2.6/dist-packages/twisted/internet/defer.py", line 944, in unwindGenerator return _inlineCallbacks(None, f(*args, **kwargs), Deferred()) --- <exception caught here> --- File "/usr/lib/python2.6/dist-packages/twisted/internet/defer.py", line 823, in _inlineCallbacks 3, in _inlineCallbacks result = g.send(result) exceptions.AttributeError: 'NoneType' object has no attribute 'send'
Hi Stephan,
from what I understand @defer.inlineCallbacks is used to defer a async callback function like how i'm using CallLater below, but where is the yield supposed to be? and how should the below code change?
The advantage defer.inlineCallbacks offers is allowing one to wait on the result of a deferred inline, without having nested blocks and closures. So a generic example could be: Without inlinecallbacks: def my_func(): d = func_returnning_deferred() def handle_result(r): # do something with r .. d.addCallback(handle_result) return d With inlineCallbacks: @defer.inlineCallbacks def my_func(): r = yield func_returning_deferred() # do stuff with r You can see that we don't have to have a nested block with an addCallback. Here the yield returns the result that we'd get in our callback function (an likewise if the errBack fired we've have to wrap that line in a try/except to handle it). Keep in mind however that @defer.inlineCallbacks can only wrap generators (functions with a yield statement in it)
def connectionMade(self): logging.debug("connection made timeout = %d", self.timeout) @defer.inlineCallbacks def onTimer(): logging.debug("timeout triggered") self.firefoxProcess.handleProcessTimedOut() self.killProcessIfAlive() d = reactor.callLater(self.timeout, onTimer)
You can see here that onTimer is not a generator (there's no yield) so defer.inlineCallbacks cannot be applied to it. Also, based on what you're doing you don't need it (i.e. you can just remove it). Keep in mind that callLater doesn't return a deferred - it just schedules onTimer to be called at a later time, which makes sense for what you are doing. If for some reason you want a deferred, I'd take a look at twisted.internet.task.deferLater. Hope that helps, Reza -- Reza Lotun mobile: +44 (0)7521 310 763 email: rlotun@gmail.com work: rlotun@twitter.com @rlotun
thanks Reza, Can you think of any reason why I would want a deferred in my scenerio? Stephan On Thu, Sep 1, 2011 at 12:31 AM, Reza Lotun <rlotun@gmail.com> wrote:
Hi Stephan,
from what I understand @defer.inlineCallbacks is used to defer a async callback function like how i'm using CallLater below, but where is the yield supposed to be? and how should the below code change?
The advantage defer.inlineCallbacks offers is allowing one to wait on the result of a deferred inline, without having nested blocks and closures. So a generic example could be: Without inlinecallbacks: def my_func(): d = func_returnning_deferred() def handle_result(r): # do something with r .. d.addCallback(handle_result) return d With inlineCallbacks: @defer.inlineCallbacks def my_func(): r = yield func_returning_deferred() # do stuff with r You can see that we don't have to have a nested block with an addCallback. Here the yield returns the result that we'd get in our callback function (an likewise if the errBack fired we've have to wrap that line in a try/except to handle it). Keep in mind however that @defer.inlineCallbacks can only wrap generators (functions with a yield statement in it)
def connectionMade(self): logging.debug("connection made timeout = %d", self.timeout) @defer.inlineCallbacks def onTimer(): logging.debug("timeout triggered") self.firefoxProcess.handleProcessTimedOut() self.killProcessIfAlive() d = reactor.callLater(self.timeout, onTimer)
You can see here that onTimer is not a generator (there's no yield) so defer.inlineCallbacks cannot be applied to it. Also, based on what you're doing you don't need it (i.e. you can just remove it). Keep in mind that callLater doesn't return a deferred - it just schedules onTimer to be called at a later time, which makes sense for what you are doing. If for some reason you want a deferred, I'd take a look at twisted.internet.task.deferLater. Hope that helps, Reza -- Reza Lotun mobile: +44 (0)7521 310 763 email: rlotun@gmail.com work: rlotun@twitter.com @rlotun
_______________________________________________ Twisted-web mailing list Twisted-web@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web
On 09/01/2011 08:39 AM, Stephan wrote:
thanks Reza,
Can you think of any reason why I would want a deferred in my scenerio?
Whenever there is a result which isn't available yet, you usually want a deferred in Twisted. In your case, you want a deferred that will give the output of the process, or an error if a timeout occurs. This should really be asked on the main twisted list, not the twisted-web list, as it's not web-specific.
participants (3)
-
Phil Mayers
-
Reza Lotun
-
Stephan