 I'd like to introduce Yuri Silivanov. He's a Python Core developer and the main developer behind Async.io, coming from Toronto, Canada to us. Giving a warm welcome, please. So this talk is an introduction to Async.await and to Async.io. It's nothing like my previous talk. This talk is basically for beginners. So a little bit about me. I'm co-founder of Magic Stack. Check out our website, it's magic.io. I'm an avid Python user since 2008, and I believe I just started to use Python 3 when it was alpha 2. And I never looked back, so use Python 3. I'm C Python Core developer since 2013. I think I started to contribute a little bit earlier than that, but still. I co-authored with Brett Cannon and Larry Hastings PEP 362, that's Inspect Signature API. I authored and implemented PEP 492, that's Async.await syntax. I maintain Async.io with Guida and Victor Stiner, and I also created UV loop and Async.pg. Let's talk about coroutines in Python. So there are five obvious ways to do coroutines in Python. The first one is callbacks and deferts, not exactly coroutines, but a way to do asynchronous programming. Twisted has been with us since 2002, and I believe that's the first time you could actually do asynchronous programming in Python reliably. Then we had Stackless Python and GreenLets. I'm pretty sure everybody knows libraries like GEvent and Event.let, those are great examples of how you integrate those features. Since Python 2.5, we could create coroutines using generators and yield expressions. Although there were some limitations with this approach and the second one is that you couldn't actually use return statement in generators. Since Python 3.3, we had yield from syntax and Async.io, for instance, uses that extensively, and in Python 3.5, we have Async.await. So let's take a look how coroutines using yield expression look like. This is an actual example from twisted documentation, old grumpy coroutine, but if you look at the source code, you will see that there is no return statement that you have to kind of use a function to return. What this function does internally, it raises an exception, and that's how this limitation that you cannot use return statement was overcome. Yield from coroutines, since Python 3.3, they look much better actually. It's recommended to use Async.io coroutine decorator, but it's not required, and you have to use yield from. A lot of problems with this approach were that people actually didn't understand what yield from is and when to use it and how it actually works, why do you have to put from? So we had some friction with actually teaching people how to use that. And also there is a slight problem with generators in general. If you don't use this Async.io decorator, let's say you have the first version of your software with a coroutine called compute, and you compute something, you sleep for one second and then you return a result. And in version two, you decided to make your users happy and you removed the Async.io sleep call, and now your function doesn't have any yield from, now it's not a coroutine, now it's not a generator, so your program just breaks. To ensure it never happens, you have to use Async.io coroutine, but some people forget. And also when you look at a lot of source code and you see lots and lots of yield from, you kind of stop understanding where are the coroutines and what are the generators and how they interact with each other, so it's all been a mess. And in Python 3.5, we introduced Async.await. So we don't need any decorators now. Even if the function doesn't have any awaits inside, we know it's a coroutine because of the Async keyword. So why do I think that Async.await is the answer how you do coroutines in Python? Well, first of all, first time in Python history, we have dedicated syntax for coroutines. It's concise, it's readable, it's easy to teach people how to use. We also have new built-in type for coroutines, again, for the first time in Python history. It's not a subtype of generators anymore. It's its own thing. We also have Async 4 and Async with constructs and these, I believe, are kind of unique to Python. I know no other language that allows you to do that kind of magic. And Async.await is actually a generic mechanism. A lot of people think that Async.await is somehow tied to Async.io. It's not true. Actually, Tornado uses Async.await now. Twisted is about to start using Async.await. And there is another framework called Curio, which uses Async.await in an entirely different way from Async.io. And also, Async.await is fast. If you write, let's say, a Fibonacci function and time coroutines versus normal functions, you will see that coroutines are only two times slower, which is totally fine, because even in large Async.await applications, you usually have 50, maybe 100 await calls per request, but you have thousands or hundreds of thousands of normal calls. So use Async.await extensively. Don't worry about it. It won't show up in your profile results. And also, Async.await is faster. Async.await and yield from are faster actually than the old approach of using yield coroutines because a lot of logic of how you actually resume and how you push values to coroutines before had to be implemented in Python and now it's a job of Python interpreters to do that, so it's much, much faster. So coroutines are subtypes of generators, but not in Pythonic sense. In Pythonic sense, generators and coroutines are completely different objects. They are subtypes in terms of C implementation. They actually share the same C struct layout. They share a lot of code, like 95% code is shared. And it actually shows if you disassemble a coroutine, you will see that it still uses yield from opcode. We also have types coroutine decorator that allows you to transform any generator into a coroutine so it's compatible with Async.await syntax. And we have a bunch of protocol methods for asynchronous generators and asynchronous context managers and future-like objects. You can read about all of that in PEP492. So how does Async.await code look in real life? It's quite easy, actually. You can see that we have a serious coroutine, then we have an await call that prepares a database statement, then we enter a transaction. And here is an interesting part about Async with, actually, it allows you to execute some asynchronous code when you enter the block and allows you to execute some asynchronous code when you exit the block. So it's quite important, actually, to implement database drivers where you kinda need this functionality. And then you can see Async4, which is also unique. It allows you to call asynchronous code while iterating. So in this example, actually, cursor prefetches you some data, you iterate over it efficiently, and when you iterate over all prefetched data, it prefetches even more. So it's quite efficient to iterate over large data sets. Now let's talk about Async.await. So it's developed by Guido himself, at least initially. A lot of people think that Async.await is a framework. I think it's a little bit wrong term to use for Async.await. I view it more as a toolbox, as a collection of tools for framework creators to use. For instance, Async.await doesn't have any HTTP implementation. It doesn't have any database drivers. You have to install libraries to do that. It's also part of standard library. And this is both good and bad. It's good because if something ends up being in standard library, it will be supported. It will be supported until the end of time, basically. So it's a good and stable business decision to actually use Async.await. Python also has a huge collection of buildbots for different platforms and different operating systems. And it's quite important to actually test Async.await on all of them, because I.O. is really hard. And another exciting point about Async.await is that Twisted soon and Tornado right now, they can actually integrate with Async.await. So in theory, you can actually run existing Tornado applications on top of Async.await event loop. So the code reuse possibilities are just exciting. So what's inside Async.await? So we have standardized and pluggable event loop. You can actually swap the event loop implementation with something else if you want. Async.await also defines a bunch of interfaces for protocols and transports. It has factories for creating servers and connections and streams. It also defines futures and tasks. Futures are essentially a bridge between old callback style code and new Async.await style code. And task is also something really fundamental. Task is what we actually call Coroutine Runner. It's something that allows event loop to actually run coroutines, to suspend them, to push values into them and resume them. And then we have APIs to create and interact with sub-processes asynchronously. We have queues, and we have synchronization primitives like locks, events, semaphores, and other stuff. And Async.await is simple. This is actually a screenshot of Async.await documentation. It only takes you five days to read it and a couple of months to digest. And then you can actually use it. So joking aside, we know that this is wrong. We will improve Async.await documentation pretty much soon. And I'm going to prove you, actually, that Async.await is simple. You only need to know about seven functions to use Async.await every day. You don't need all this stuff in Async.await. Again, that's a toolbox. That's for framework creators. To use Async.await, you only need to know this. So let's go over those functions. The first one, the first function that you have to know is Async.await get event loop. It returns you an instance of the actual event loop. And you have to worry about event loop when you launch the program. But then it actually disappears. You aren't required to know about what kind of event loop you have. Just use Async.await throughout your program. That's it. So you get an Async.await event loop. That's the first highlighted line. And then you call create task. So what create task does, it wraps coroutines into those task objects that allow event loop to actually run them. So what we have here, we have a coroutine called say coroutine function, which waits for when number of seconds and then prints what. So what we do next after we get the event loop, we create two tasks for the same coroutine function. So essentially it will be two different coroutines. One will say hello after half a second. And another one will say world after one second. And the last line is loop run forever. And what it does, it literally loops run forever until you actually terminate it somehow, maybe with control C, maybe some other way. The next function, which is actually quite important, is Async.await gather. And what it allows you to do, it allows you to wait on several coroutines simultaneously. And it actually returns when all of them are resolved. So we modified our example a little bit. Now we wrap two tasks to say hello and to say world with Async.await gather and we use run until complete. That's another method of event loop. It accepts tasks, futures, coroutines, and it basically runs the event loop until the task is complete. So here we will end the execution when both coroutines are finished. Another important function is loop run in executor. So if you have a bunch of old code, let's say, or that uses blocking IO, or you have some very computationally intensive code, you can actually run that code in a thread or in a process pool and await on it. So it's actually quite handy. And the API itself is pluggable. If you pass none as a first argument, it uses the Async.io internal thread pool, but you can actually pass any kind of pool executor from concurrent futures module. And the last very important function is loop close. So all of our previous examples were kind of wrong because we didn't close the loop. So the correct way is to actually run event loop and then finally block, you have to close it. The close function, close method, it actually cleans up the resources and it will print out warnings if something goes wrong. And how, especially when you start learning Async.io, you should always use Async.io debug mode. To enable it, you can just have an environment variable called Python Async.io debug or you can enable debug mode programmatically, just call loop set debug. It's very important to use Async.io debug mode. It will uncover a lot of bugs in your code and it will make programming with Async.io much easier. Also make sure that logging in Python is configured properly and you can actually see the errors in STD out or STDR because sometimes it's not and people just ask questions like I don't see any errors in program output. Async.io uses logging module so if it's not configured properly, you won't see anything. And also configure a test runner to print warnings. For instance, PyTest somehow doesn't do that by default and warnings are actually quite important. For instance, if you have a coroutine and you just forgot to put await, you just use the regular function call. Then essentially your program will do nothing because without await, it will just create a coroutine and that's it, it will never be scheduled. So Python is actually emitting a resource warning if situation like this happens. So make sure you actually see warnings. Let's quickly talk about UV loop. UV loop is an alternative implementation of Async.io event loop. It's written in Scython and by the way, I really recommend you all to check out Scython. It's an amazing language. It looks like Python but it compiles to C. It uses LibUV under the hood and LibUV is the event loop for Node.js and that's a good thing because Node.js is extremely widespread. It's well tested and LibUV is really fast. And also UV loop defines its own implementation of futures and tasks so that even if you don't do a lot of IEO, your Async await calls will just become faster. So how fast is UV loop? It's in a simple benchmark like an echo server, it's actually quite a bit faster than Async.io. It's two to four times faster. If you have lots and lots of Python code, it won't be as exciting but I heard a lot of reports that UV loop even for big production applications can give you boost of 30, 40%. And even if you don't have a high load on your server, I still recommend you to use UV loop just because the quality of service improves, you will have smaller latencies. So it's faster than Async.io but it's also faster than any other Python framework on Python 3 specifically. We're not talking about PyPy, Twisted, all this stuff. On Python 3, UV loop and Async.io are the fastest and the simple echo server implementation, it's actually as fast as Go echo server implementation. And it's somehow twice as fast as Node.js echo server implementation, which I personally find amazing because, well, Node.js itself uses UV and itself is written mostly in C++ and C. Let's quickly talk about PEP 492. So I actually had this idea in 2014 and I quickly and briefly discussed it with Guida and Victor Steiner, but we didn't have good arguments to actually think about this idea even more. But in 2015, I approached Guida at the language summit and I told him, let's do Async.await. People really don't know how to use yield forms. They don't understand that. And also just decides doing Async.await. Let's introduce Async.for and Async.with because otherwise it's just impossible to write like good looking code for entering a database transaction. You have to use try, finally call, and rollback and manually call commit. And people just don't know how to do this. So it's not convenient. So actually he liked this idea and he encouraged me a lot because it was just two months before complete feature freeze in Python for Python 3.5 release. So I spent about two, maybe five days for the first draft of the PEP. About three hours to prototype and see Python. And then I published my first draft on Python ideas. And it was very, very positively received. So after about 500 emails on Python ideas and Python dev, we had a PEP that everybody liked. And then Nick Coglian and Victor Steiner helped me with reviewing the patch and actually pushing it in production. So why am I telling you all this? Is that I want you to do the same. I want you to actually, if you want something to do in C Python, some new feature, go ahead and propose it to Python ideas. Before that, please Google that this feature wasn't proposed before. And if it wasn't, just go ahead and do it. The process from that is quite simple. You will discuss it on Python ideas with other Python users and Python core developers. If they like this idea, they will probably ask you to write a PEP, which is also quite an easy thing. We have hundreds of PEPs available now. You can read through them. You can see the common structure. So you document your feature idea. And then there is another process. You will discuss this PEP. Then you implement this PEP or you can find some other core developer who will do that for you. For instance, the metrics multiplier operator, the at sign was added to Python 3.5. The PEP initially was written by Nathaniel Smith, but the implementation was written by Benjamin Patterson. So you don't even need to know low-level C Python programming. If you have an exciting idea, somebody will implement it. That's it. Thank you. Thank you very much for this talk. We have time for questions. And if they are building context manager for this try finally event loop termination thing. You showed to say to try finally terminate event loop. Is there a context manager for this? No, we don't have context manager. But it would make sense to have a context manager for this? Maybe, but you use it only once in your program, usually. So that's why we are thinking that maybe it's not worth adding extra complexity to how you work with event loop. Because actually you can only close event loop once. Once you close it, you cannot resume it. So if you use this context manager twice, it will break your program. So we decided that let's make it explicit. In async IO? Maybe, yeah. Yeah, if UV loop is much faster and a drop in replacement for async IO, why don't we just replace async IO in the core Python with UV loop rather than force the users to ask the users to replace it as an optimization? So the question is why don't we just put UV loop in C Python, right? There are a couple of problems with that. First of all, UV loop is written in Python and we don't merge anything written in Python to C Python just because we don't want to introduce extra build dependencies in C Python. So to do that, we'll have to rewrite it in C. And I think it's quite doable, actually, because when I was working on UV loop, it was actually hard to figure out how to correctly manage LibUV low-level resources and Python high-level abstractions together so that garbage collection works correctly, stuff like that. I think now I actually have pretty good understanding how to write the system in accuracy but before that it just wasn't feasible just because it's much easier to refactor code in C Python. So to merge UV loop or to recreate it in C Python, we will need to write it in pure C and also have to make a decision of making LibUV a dependency of C Python, which probably a lot of other core developers we will have some concerns with. But I think maybe eventually we'll do that. Hi, is it possible, have you published your benchmark scripts that you've actually done? They're all available at GitHub. It's a reaper called VMbench. It's on the same account as UV loop. Everything is there. Benchmarks are quite generic. You have a script to run them all and what they actually do, they launch a Docker instance and they launch each framework there. They warm up there in the Docker instance. So it's all automated. So you definitely can do it yourself. You can run them. Hi, regarding the documentation issue, so if you're gonna hold a sprint on that topic, I would gladly contribute for the basic examples because I'm really struggling with what's currently available. Sure, I would really appreciate it. I'm not sure if I will be on the sprints. If I'm not, just create an issue with Python bug tracker and I'll definitely take a look. Any more questions? Okay, I think that's it. Ben, thank you very much again. Thank you guys.