<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta content="text/html; charset=windows-1252"
 http-equiv="Content-Type">
</head>
<body bgcolor="#ffffff" text="#000000">
<br>
<blockquote cite="mid:1B323C5E-54F3-4EFC-9911-460C22068DC6@gmail.com"
 type="cite">
  <pre wrap="">You are currently considering your task from the viewpoint “let's make a callback chain for the perfect workflow and alter this chain in case of anything going wrong.” I think the flaw with this approach is that you are trying to make your “ideal” flow work at all in situations where it would, in fact, fail for the most of the time.
  </pre>
</blockquote>
<br>
I am not sure I <span id="result_box" class="short_text"><span
 style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);"
 title="">understand you correct...<br>
</span></span>In my case when ASR or TTS resource aquiring fails I must
transfer call to operator but not fail.<br>
<br>
<blockquote cite="mid:1B323C5E-54F3-4EFC-9911-460C22068DC6@gmail.com"
 type="cite">
  <pre wrap="">Instead of this, you could try to reconsider the task from the viewpoint of “Let's not add any further callbacks until this is absolutely necessary”. For me, this approach rather works. Moreover, you have the means to do it — a callback can return a Deferred, and so on.
  </pre>
</blockquote>
<br>
Good. But I need to start acquiring ASR and TTS resources at one time
(in parallel) because this is very long operations and they can be good
performed in parallel because physically this is other and different
machines (computers). Why we can't do that way? Why user have to wait
double time?<br>
<br>
But it doesn't matter, because there is one more thing. Ok. Let's do it
sequentially. But what if user hangs up call? I want to <span
 id="result_box" class="short_text"><span
 style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);"
 title="">immediately stop all deferred processes due I don't need
their results any more. All yields inside inlineCallbacks must raise
some exception.<br>
<br>
This code shows how we can solve this with feautures described<br>
<br>
class DestroyingError(Exception):<br>
    pass<br>
<br>
class DestroySupport(object):<br>
    __destroying_deferreds = None<br>
<br>
    def __get_destroying_deferred(self):<br>
        if self.__destroying_deferreds is None:<br>
            self.__destroying_deferreds = {}<br>
        deferred = defer.Deferred()<br>
        self.__destroying_deferreds[deferred] = 1<br>
        # delete "destroying" `deferred` when it finished<br>
        deferred.addFinalizer(self.__destroying_deferreds.pop, deferred)<br>
        return deferred<br>
<br>
    def wait_destroying(self):<br>
        return self.__get_destroying_deferred()<br>
<br>
    def destroy(self):<br>
        if self.__destroying_deferreds is not None:<br>
            # errback all "destroying" deferreds<br>
            for deferred in self.__destroying_deferreds.keys():<br>
                try:<br>
                    raise DestroyingError('destroying')<br>
                except DestroyingError:<br>
                    deferred.errback()<br>
            self.__destroying_deferreds = None<br>
<br>
# See InlineCallbacksManager in my first message in current discussion
thread<br>
class
InlineCallbacksWithDestroySupportManager(defer.InlineCallbacksManager):<br>
    def __init__(self, instance, *args, **kw):<br>
        defer.InlineCallbacksManager.__init__(self, instance, *args,
**kw)<br>
        # chain Deferred with "destroying" deferred<br>
        # therefore if destroy occurs Deferred errbacked with
DestroyingError<br>
        destroying_deferred = instance.wait_destroying()<br>
        destroying_deferred.chainDeferred(self.deferred)<br>
        # unchain if deferred finished (<br>
        #     "destroying" deferred will be cancelled also due not used
any more<br>
        #     and deletes from DestroySupport.__destroying_deferreds
due to its finalizer<br>
        #     we add in DestroySupport.__get_destroying_deferred<br>
        # )<br>
       
self.deferred.addFinalizer(destroying_deferred.unchainDeferredSafe,
self.deferred)<br>
<br>
inlineCallbacksWithDestroySupport =
defer.create_inline_callbacks_decorator(<br>
    InlineCallbacksWithDestroySupportManager<br>
)<br>
<br>
class CallService(DestroySupport):<br>
    def on_connection_lost(self):<br>
        # launch cascade destroying when connection lost!!!<br>
        # all yields will raise DestroyingError<br>
        # therefore all resources will be released<br>
        self.destroy()<br>
<br>
    @inlineCallbacksWithDestroySupport<br>
    def incoming_call(self, call):<br>
        call.start_play('music')<br>
        try:<br>
            # tuple yield feature!<br>
            tts, asr = yield self.acquire_tts_for(call),
self.acquire_asr_for(call)<br>
        except TimeoutError:<br>
            # this occurs if TimeoutError raises in
`self.acquire_tts_for` or `self.acquire_asr_for`<br>
            # i.e. DeferredList(..., fireOnOneErrback=1)<br>
            call.transfer_to('operator')<br>
            # NOTE: at this point I want to automatically _recursively_
(in deep)<br>
            # stop all deferred processes<br>
            # starting inside `self.acquire_tts_for` and
`self.acquire_asr_for`<br>
            # because I don't need their results (and expensive
resources!) any more<br>
        except DestroyingError:<br>
            log('Destroying detected')<br>
            raise<br>
        else:<br>
            try:<br>
                try:<br>
                    yield tts.speak('what you want? say me after
signal')<br>
                    yield asr.start_recognition()<br>
                    call.start_play('signal')<br>
                    result = yield asr.wait_recognition_result()<br>
                    ... do something with result ...<br>
                except DestroyingError:<br>
                    log('Destroying detected')<br>
                    raise<br>
            finally:<br>
                tts.release()<br>
                asr.release()<br>
<br>
    @inlineCallbacksWithDestroySupport<br>
    def acquire_tts_for(self, call):<br>
        # may raise TimeoutError inside<br>
        tts_connection = yield self.tts.acquire_connection(timeout=10)<br>
        try:<br>
            # may raise TimeoutError inside<br>
            yield call.make_audio_link_with(tts_connection, 'in',
timeout=10)<br>
        except Exception:<br>
            # we MAY receive this Exception when cascading cancellation
occurs<br>
            tts_connection.release()<br>
            raise<br>
        else:<br>
            return tts_connection<br>
<br>
    @inlineCallbacksWithDestroySupport<br>
    def acquire_asr_for(self, call):<br>
        # may raise TimeoutError inside<br>
        asr_connection = yield self.asr.acquire_connection(timeout=10)<br>
        try:<br>
            # may raise TimeoutError inside<br>
            yield call.make_audio_link_with(asr_connection, 'out',
timeout=10)<br>
        except Exception:<br>
            # we MAY receive this Exception when cascading cancellation
occurs<br>
            asr_connection.release()<br>
            raise<br>
        else:<br>
            return asr_connection<br>
<br>
</span></span>
</body>
</html>