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