[Python-ideas] Async API

Yury Selivanov yselivanov.ml at gmail.com
Fri Oct 26 19:06:14 CEST 2012


On 2012-10-26, at 12:57 PM, Itamar Turner-Trauring <itamar at futurefoundries.com> wrote:
> On Fri, Oct 26, 2012 at 12:36 PM, Guido van Rossum <guido at python.org> wrote:
> On Fri, Oct 26, 2012 at 8:52 AM, Laurens Van Houtven <_ at lvh.cc> wrote:
> > err, I suppose the missing bit there is that you'll probably want to:
> >
> > reactor.callLater(timeout, d.cancel)
> >
> > As opposed to calling d.cancel() directly. (That snippet was in
> > bpython-urwid with the reactor running in the background, but I doubt it'd
> > work well anywhere else outside of manholes :))
> 
> So I think that Yuri's original problem statement, transformed to
> Twisted+Deferred, might still apply, depending on how you implement
> it. Yuri essentially did this:
> 
> def foobar():  # a task
>     try:
>         yield <blocking action>
>     finally:
>         # must clean up regardless of whether action succeeded or failed:
>         yield <blocking cleanup>
> 
> He then calls this with a timeout, with the semantics that if the
> generator is blocked in a yield when the timeout arrives, that yield
> raises a Timeout exception (and at no other time is Timeout raised).
> The problem with this is that if the action succeeds within the
> timeout, but barely, there's a chance that the cleanup of a
> *successful* action receives the Timeout exception. Apparently this
> bit Yuri. I'm not sure how you'd model that using just Deferreds, but
> using inlineCallbacks it seems the same thing might happen. Using
> Deferreds, I assume there's a common pattern to implement this that
> doesn't have this problem. Of course, using coroutines, there is too
> -- spawn the cleanup as an independent task.
> 
> If you call cancel() on a Deferred that already has a result, nothing happens. So you don't get a TimeoutError if the operation has succeeded (or failed some other way). This would also be true when using inlineCallbacks, so there's no issue.
> 
> In general I'm not clear why this is a problem: in a single-threaded program only one thing happens at a time. Your code for triggering a timeout always has the option to check if the operation has succeeded, without worrying about race conditions.

Let me ask you a question that may help me and others to understand
how inlineCallbacks works.

If you write the following:

   def func():
       try:
           yield one_thing()
           yield and_another()
       finally:
           yield and_finally()

Then each of those yields will create a separate Deferred object, that
'inlineCallbacks' transparently dispatches via generator send/throw,
right?

And if you 'yield func()' the same will happen--'inlineCallbacks' will
return a Deferred, that will have a result of 'func' execution?

Thanks,
Yury


More information about the Python-ideas mailing list