Python 3, an Illustrated Tour Transcripts
Chapter: Asynchronous Programming
Lecture: asyncio Context Managers

Login or purchase this course to watch this video and the rest of the course contents.
0:00 In this video we're going to talk about asynchronous context managers. These were discussed in pep 492, it came out in Python 3.5.
0:08 Let's look at the protocol for asynchronicity in Python 3. There's a couple protocols that you can implement.
0:15 If you want to define your own class that behaves as a coroutine, you can implement the __await__ method
0:21 that means that you can call await on it and get the results from that. You can also Implement an asynchronous iterator and we'll talk about those,
0:29 and in this section, we're going to talk about asynchronous context managers. So they have a __aenter__ and a __aexit__ method.
0:36 If you're not familiar with context managers, let's briefly discuss what traditional context managers look like,
0:41 again in Python, these are things that you can put in a with statement so I can open a file with a with statement
0:48 and within the indented portion of that block the file is going to be opened and when I exit out of it, it's going to close it for me.
0:55 So the traditional thinking about when to use a context manager is if you have some logic you want to insert before a block
1:02 and some logic that you want to insert after, either one of those makes a good case for using a context manager.
1:07 And in that case you implement a __enter__ and a __exit__ method. Here's a brief example of doing that.
1:14 Here's a silly little context manager called runner. We're going to pass an item into the constructor and then when we enter into the block,
1:21 we're going to assume that the item that we passed in is a dictionary and we're going to set the key running to true,
1:25 when we exit out of that block, we will set the key running to false. Down here below we can see we're actually running this
1:32 and you can see that here inside of this block while we are in this block, the running key on item is set to true
1:39 as soon as we unindent out of that block running is set to false. So right at the start this with corresponds to this __enter__ method
1:47 and when we unindent down here, right before this guy, we are at the __exit__ method, right there.
1:54 So that's how you can control inserting logic before and after. On this page, I'm linking to a nice little project.
2:01 It's a Linux window manager utility, but it allows you to run external processes asynchronously in a context manager.
2:11 Let's look at some of the content of that and we can see that it implements this context manager for asynchronous context managers protocol.
2:18 You can see that there's a __aenter__ method and interestingly enough, because it's defined with async here, we can call await within it.
2:26 So that's sort of the key to the asynchronous context manager is within aenter and aexit, you can call asynchronous co-routines as well.
2:35 And this _AIOPopen guy who again runs a processed asynchronously is awaitable, so we see that he has a __await__ method defined as well.
2:46 And in the constructor here you pass in a coroutine, he's yielding from that and returning from that inside of the aenter there.
2:54 I am not going to dig into much more of what's going on, but I just wanted to make you aware of key difference
2:59 between an asynchronous context manager and a normal context manager. So if you need to do logic from a coroutine inside an enter or exit block,
3:09 you would use an asynchronous context manager because inside of there you can call await. Here's another example, we're just going to look at quickly.
3:16 This is heavily inspired by the async timeout project, I'll link there to the GitHub, but we're defining a class called timeout
3:23 and we're going to use a timeout context manager to be able to timeout co-routines. Here we're just going to show the constructor
3:31 we can pass in a timeout value, how long we want to timeout, we'll pass in a loop. Inside of it, we're going to make it canceled attribute
3:37 that says we have not cancelled what we're running and a handler, this handler will be called if we're going to cancel what's running.
3:44 Here we see the asynchronous protocol being implemented, we see an async def __aenter__, so that's when we enter the block
3:54 and you can see that when we enter the block, we're going to get the current time on the loop and we're going to add the timeout to it.
4:01 And that's when we want to timeout this block. We're going to get a task, we'll show the get task implementation below
4:06 and we'll set a handler, on the loop we'll say call at at some time when we're going to call our self cancel method and that gives us back a handler.
4:16 We also have the __aexit__ and you can see that this is very analogous to the traditional context manager,
4:24 we get back an exception and a value and a traceback if there was an exception thrown from within the context
4:31 and here you can see that if the canceled flag is set, then we raise the asyncio timeout error,
4:37 otherwise, if we have a handler we're going to cancel that handler and set the handler to none and we're going to set the test to none.
4:44 Here we have the implementation of the cancel method. It just calls cancel on the task and sets cancel to true.
4:50 Down below we have the get task function that takes a loop and returns the current task in the block. Here's an example of running that,
4:58 we can see when we use an asynchronous context manager instead of saying just with we have to say async with.
5:05 Here we're saying that we're going to time out after two seconds. We're going to pass in our loop as well,
5:10 and we're just going to sleep for one second here. So this will not timeout, it should print done and after.
5:15 If we change this value in here from 1 to say 3 or some value greater than 2, then this would time out and instead of saying done here,
5:24 it would print timeout and it's going to raise this asyncio timeout error. We can handle that with a try block if we want to
5:32 and do the appropriate thing at that point. So this is a simple example of using a context manager that is asynchronous.
5:40 In this video we talked about asynchronous context managers. Again, the key here is that you implement the protocol
5:46 and the key difference between these context managers and traditional context managers is that you can await from within them.

Talk Python's Mastodon Michael Kennedy's Mastodon