Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!feeder.erje.net!eu.feeder.erje.net!newsfeed.xs4all.nl!newsfeed1.news.xs4all.nl!xs4all!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.130 X-Spam-Level: * X-Spam-Evidence: '*H*': 0.74; '*S*': 0.00; 'algorithm': 0.04; 'buttons': 0.09; 'def': 0.12; 'gui': 0.12; 'canceled': 0.16; 'corresponds': 0.16; 'hypothetical': 0.16; 'opposite': 0.16; 'perceived': 0.16; 's/he': 0.16; 'simple.': 0.16; 'code.': 0.18; 'bit': 0.19; 'basically': 0.19; 'seems': 0.21; '(the': 0.22; 'programming': 0.22; 'comfortable': 0.22; 'instead.': 0.24; 'skip': 0.24; 'decide': 0.24; 'fine': 0.24; 'looks': 0.24; 'sort': 0.25; "i've": 0.25; 'task': 0.26; 'this:': 0.26; 'header:In-Reply- To:1': 0.27; 'possibility': 0.29; 'thus': 0.29; '???': 0.30; 'reaches': 0.30; 'message-id:@mail.gmail.com': 0.30; "i'm": 0.30; 'work.': 0.31; 'code': 0.31; 'that.': 0.31; '"do': 0.31; 'breaking': 0.31; 'complete,': 0.31; 'probably': 0.32; 'stuff': 0.32; 'option': 0.32; 'run': 0.32; 'running': 0.33; 'trouble': 0.34; "i'd": 0.34; 'something': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'there': 0.35; 'cancel': 0.36; 'executing': 0.36; 'reality': 0.36; 'vice': 0.36; 'yield': 0.36; 'doing': 0.36; 'similar': 0.36; 'should': 0.36; 'so,': 0.37; 'two': 0.37; 'being': 0.38; 'gmail': 0.38; 'tasks': 0.38; 'to:addr :python-list': 0.38; 'little': 0.38; 'skip:& 20': 0.39; 'structure': 0.39; 'sure': 0.39; 'to:addr:python.org': 0.39; 'enough': 0.39; 'how': 0.40; 'future': 0.60; 'catch': 0.60; 'signal': 0.60; 'simple,': 0.60; 'new': 0.61; 'world.': 0.61; 'simple': 0.61; 'first': 0.61; 'complete': 0.62; 'real': 0.63; 'world': 0.66; 'roles': 0.68; 'started.': 0.68; 'statement,': 0.68; 'facilities': 0.69; 'user,': 0.69; 'safe': 0.72; 'obvious': 0.74; 'guaranteed': 0.75; '100%': 0.77; '"do': 0.84; 'calculations': 0.84; 'guts': 0.84; 'activity,': 0.91; 'execution,': 0.91; 'notion': 0.91 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :content-type; bh=ibV1csAb0gLt1gC5Xhyhz8blFWKeAmJ4EpSwkPge+zA=; b=jAL5uXoEK6kL1+Pr4HMP8Ioppes37piV/drMcrH36R+F2JU65TUoksPOVxxjkJjvFc 6HcXKCkCdUklsA5F4lIzD4Whj2jvrh0ufwdfJKead0e4NS6LtKUWnjbrpRmD61cMKzI+ 6a2gz+Q1ANExz4qb2o8fgANDHLO8oNL5yPCOv6wgd8CJsXNtOPxtw56V/8EIfB8pEqmA xjH/qSeuzfLMgUs9a4QSa5bKVwuUsaUA5vwJqgNhP0GCFxnH6kIAbpUDzvlm4kCX/PLb yYeR6khwmhaWBHePJoNpIYZSf5gaBdIwxN0h/bzOjJYTtFWmLCr/fqoMbJLnAP55c9Nf f72g== MIME-Version: 1.0 X-Received: by 10.50.43.134 with SMTP id w6mr22874700igl.20.1389056160342; Mon, 06 Jan 2014 16:56:00 -0800 (PST) In-Reply-To: References: Date: Mon, 6 Jan 2014 18:56:00 -0600 Subject: Trying to wrap my head around futures and coroutines From: Skip Montanaro To: Python Content-Type: multipart/alternative; boundary=089e01184b0c03546104ef56d7f3 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 149 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1389056165 news.xs4all.nl 2838 [2001:888:2000:d::a6]:59194 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:63389 --089e01184b0c03546104ef56d7f3 Content-Type: text/plain; charset=UTF-8 I've been programming for a long while in an event&callback-driven world. While I am comfortable enough with the mechanisms available (almost 100% of what I do is in a PyGTK world with its signal mechanism), it's never been all that satisfying, breaking up my calculations into various pieces, and thus having my algorithm scattered all over the place. So, I'm looking for a little guidance. It seems to me that futures, coroutines, and/or the new Tulip/asyncio package might be my salvation, but I'm having a bit of trouble seeing exactly how that would work. Let me outline a simple hypothetical calculation. I'm looking for ways in which these new facilities might improve the structure of my code. Let's say I have a dead simple GUI with two buttons labeled, "Do A" and "Do B". Each corresponds to executing a particular activity, A or B, which take some non-zero amount of time to complete (as perceived by the user) or cancel (as perceived by the state of the running system - not safe to run A until B is complete/canceled, and vice versa). The user, being the fickle sort that he is, might change his mind while A is running, and decide to execute B instead. (The roles can also be reversed.) If s/he wants to run task A, task B must be canceled or allowed to complete before A can be started. Logically, the code looks something like (I fear Gmail is going to destroy my indentation): def do_A(): when B is complete, _do_A() cancel_B() def do_B(): when A is complete, _do_B() cancel_A() def _do_A(): do the real A work here, we are guaranteed B is no longer running def _do_B(): do the real B work here, we are guaranteed A is no longer running cancel_A and cancel_B might be no-ops, in which case they need to start up the other calculation immediately, if one is pending. This is pretty simple execution, and if my job was this simple, I'd probably just keep doing things the way I do now, which is basically to catch a "complete" or "canceled" signal from the A and B tasks and execute the opposite task if it's pending. But it's not this simple. In reality there are lots of, "oh, you want to do X? You need to make sure A, B, and C are not active." And other stuff like that. I have this notion that I should be able to write do_A() something like this: def do_A(): cancel_B() yield from ... ??? _do_A() ... or def do_A(): future = cancel_B() future.on_completion(_do_A) ... or ??? with the obvious similar structure for do_B. To my mind's eye, the first option is preferable, since it's obvious that when control reaches the line after the yield from statement, it's fine to do the guts of task A. So, is my simpleminded view of the world a possibility with the current facilities available in 3.3 or 3.4? Thx, Skip --089e01184b0c03546104ef56d7f3 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable

I've been programming for a long while in an event&c= allback-driven world. While I am comfortable enough with the mechanisms ava= ilable (almost 100% of what I do is in a PyGTK world with its signal mechan= ism), it's never been all that satisfying, breaking up my calculations = into various pieces, and thus having my algorithm scattered all over the pl= ace.

So, I'm looking for a little guidance. It seems to me th= at futures, coroutines, and/or the new Tulip/asyncio package might be my sa= lvation, but I'm having a bit of trouble seeing exactly how that would = work. Let me outline a simple hypothetical calculation. I'm looking for= ways in which these new facilities might improve the structure of my code.=

Let's say I have a dead simple GUI with two buttons labe= led, "Do A" and "Do B". Each corresponds to executing a= particular activity, A or B, which take some non-zero amount of time to co= mplete (as perceived by the user) or cancel (as perceived by the state of t= he running system - not safe to run A until B is complete/canceled, and vic= e versa). The user, being the fickle sort that he is, might change his mind= while A is running, and decide to execute B instead. (The roles can also b= e reversed.) If s/he wants to run task A, task B must be canceled or allowe= d to complete before A can be started. Logically, the code looks something = like (I fear Gmail is going to destroy my indentation):

def do_A():
when B is complete, _do_A()
cancel_B()

def do_B():
when A is complete, _do_B()
cancel_A()

def _do_A():
do the real A work here, we are guaranteed B is no longer running

def _do_B():
do the real B work here, we are guaranteed A is no longer running

cancel_A and cancel_B might be no-ops, in which case they ne= ed to start up the other calculation immediately, if one is pending.

This is pretty simple execution, and if my job was this simp= le, I'd probably just keep doing things the way I do now, which is basi= cally to catch a "complete" or "canceled" signal from t= he A and B tasks and execute the opposite task if it's pending. But it&= #39;s not this simple. In reality there are lots of, "oh, you want to = do X? You need to make sure A, B, and C are not active." And other stu= ff like that.

I have this notion that I should be able to write do_A() som= ething like this:

def do_A():
cancel_B()
yield from ... ???
_do_A()
...

or

def do_A():
future =3D cancel_B()
future.on_completion(_do_A)
... or ???

with the obvious similar structure for do_B. To my mind'= s eye, the first option is preferable, since it's obvious that when con= trol reaches the line after the yield from statement, it's fine to do t= he guts of task A.

So, is my simpleminded view of the world a possibility with = the current facilities available in 3.3 or 3.4?

Thx,

Skip

--089e01184b0c03546104ef56d7f3--