[Python-ideas] Async API: some code to review

Steve Dower Steve.Dower at microsoft.com
Wed Oct 31 23:36:13 CET 2012


Guido van Rossum wrote:
> On Wed, Oct 31, 2012 at 2:31 PM, Steve Dower <Steve.Dower at microsoft.com> wrote:
>> Guido van Rossum wrote:
>>> Just get with the program and use yield-from exclusively.
>>
>> I didn't realise there was a "program" here, just a discussion about an API
>> design.
> 
> Sorry, I left off a smiley. :-)

Always a risk in email communication - no offence taken.

>> I've already raised my concerns with using yield from exclusively, but since
>> the performance argument trumps all of those then there is little more I can
>> contribute.
> 
> What about the usability argument? Don't you think users will be confused by the
> need to use yield from some times and just yield other times? Yes, they may be
> able to tell by looking up the definition and checking how it is decorated, but
> that doesn't really help.

Users only ever _need_ to write yield. The only reason that wattle does not work with Python 3.2 is because of non-blank returns inside generators.

There is only one reason to use 'yield from' and that is for the performance optimisation, which I do acknowledge and did observe in my own benchmarks.

>> When a final design begins to stabilise, I will see how I can make use of it
>> in my own code. Until then, I'll continue using Futures, which are ideal for my
>> current needs. I won't be forcing 'yield from' onto my users until its usage is
>> clear and I can provide them with suitable guidance.
> 
> Understood. What exactly is it that makes Futures so ideal for your current
> needs? Is it integration with threads?
> 
> Another tack: could you make use of tulip/polling.py? That doesn't use
> generators of any form; it is meant as an integration point with other styles of
> async programming (although I am not claiming that it is any good in its current
> form -- this too is just a strawman to shoot down).

I know I've been vague about our intended application (deliberately so, to try and keep the discussion neutral), but I'll lay out some details.

We're working on adding support for Windows 8 apps (formerly known as Metro) written in Python. These will use the new API (WinRT) which is highly asynchronous - even operations such as opening a file are only* available as an asynchronous function. The intention is to never block on the UI thread.

(* Some synchronous Win32 APIs are still available from C++, but these are actively discouraged and restricted in many ways. Most of Win32 is not usable.)

The model used for these async APIs is future-based: every *Async() function returns a future for a task that is already running. The caller is not allowed to wait on this future - the only option is to attach a callback. C# and VB use their async/await keywords (good 8 min intro video on those: http://www.visualstudiolaunch.com/vs2012vle/Theater?sid=1778) while JavaScript and C++ have multi-line lambda support. For Python, we are aiming for closer to the async/await model (which is also how we chose the names).

Incidentally, our early designs used yield from exclusively. It was only when we started discovering edge-cases where things broke, as well as the impact on code 'cleanliness', that we switched to yield.

There are three aspects of this that work better and result in cleaner code with wattle than with tulip:

 - event handlers can be "async-void", such that when the event is raised by the OS/GUI/device/whatever the handler can use asynchronous tasks without blocking the main thread. In this case, the caller receives a future but ignores it because it does not care about the final result. (We could achieve this under 'yield from' by requiring a decorator, which would then probably prevent other Python code from calling the handler directly. There is very limited opportunity for us to reliably intercept this case.)

 - the event loop is implemented by the OS. Our Scheduler implementation does not need to provide an event loop, since we can submit() calls to the OS-level loop. This pattern also allows wattle to 'sit on top of' any other event loop, probably including Twisted and 0MQ, though I have not tried it (except with Tcl).

 - Future objects can be marshalled directly from Python into Windows, completing the interop story. Even with tulip, we would probably still require a decorator for this case so that we can marshal regular generators as iterables (for which there is a specific type). Without a decorator, we would probably have to ban both cases to prevent subtly misbehaving programs. At least with wattle, the user does not have to do anything different from any of their other @async functions.

Despite this intended application, I have tried to approach this design task independently to produce an API that will work for many cases, especially given the narrow focus on sockets. If people decide to get hung up on "the Microsoft way" or similar rubbish then I will feel vindicated for not mentioning it earlier :-) - it has not had any more influence on wattle than any of my other past experience has.

Cheers,
Steve




More information about the Python-ideas mailing list