On Thu, Aug 22, 2013 at 5:29 AM, Neal Becker <ndbeck...@gmail.com> wrote: > So my son is now spending his days on c# and .net. He's enthusiastic about > async and await, and said to me last evening, "I don't think python has > anything > like that". I'm not terribly knowledgeable myself regarding async programming > (since I never need to use it). I did look at this: > > http://tirania.org/blog/archive/2013/Aug-15.html > > I wonder what response the python community might have.
I've done something sort of similar to await in Python using restartable functions. The code looks like this (using Twisted Deferreds, but any sort of promise could be substituted in): from functools import wraps from twisted.internet import defer def restartable(func): def resume(result, is_failure, results, args, kws): def await(get_deferred, *args, **kws): try: is_failure, result = reversed_results.pop() except IndexError: raise Await(get_deferred(*args, **kws)) if is_failure: result.raiseException() return result def do_once(func, *args, **kws): return await(defer.maybeDeferred, func, *args, **kws) await.do_once = do_once if results is None: results = [] else: results.append((is_failure, result)) reversed_results = list(reversed(results)) try: func(await, *args, **kws) except Await as exc: deferred = exc.args[0] deferred.addCallback(resume, False, results, args, kws) deferred.addErrback(resume, True, results, args, kws) @wraps(func) def wrapper(*args, **kws): return resume(None, None, None, args, kws) return wrapper class Await(BaseException): pass The usage of restartable and await then looks something like this: @restartable def random_sum(await): try: a = await(random_number) b = await(random_number) c = await(random_number) d = await(random_number) except ValueError as exc: print("Couldn't get four numbers: " + exc.message) return print('{} + {} + {} + {} = {}'.format(a, b, c, d, a + b + c + d)) The "await" argument is passed in by the restartable machinery, not by the caller. The argument passed to await is a callable that is expected to return a Deferred, and any additional arguments are passed along to the callable. A boring implementation of the "random_number" callable might look like this: def random_number(): from random import randrange from twisted.internet import defer, reactor deferred = defer.Deferred() if randrange(4) > 0: number = randrange(42) print("Generated {}".format(number)) reactor.callLater(1, deferred.callback, number) else: print("Failed") reactor.callLater(1, deferred.errback, ValueError("Not available")) return deferred Of course the big caveat to all this is that since the function is restartable, the "random_sum" function above actually gets called five times, and so if there are any side effects before the last await, they'll end up happening multiple times. This can be averted using await.do_once: @restartable def random_sum(await): try: await.do_once(print, 1) a = await(random_number) await.do_once(print, 2) b = await(random_number) await.do_once(print, 3) c = await(random_number) await.do_once(print, 4) d = await(random_number) except ValueError as exc: print("Couldn't get four numbers: " + exc.message) return print('{} + {} + {} + {} = {}'.format(a, b, c, d, a + b + c + d)) The result of running this is: 1 Generated 35 2 Generated 28 3 Generated 32 4 Generated 16 35 + 28 + 32 + 16 = 111 -- http://mail.python.org/mailman/listinfo/python-list