Trio: A pythonic way to do async programming
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 132 | |
Author | ||
License | CC Attribution - NonCommercial - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/44962 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
EuroPython 201843 / 132
2
3
7
8
10
14
15
19
22
27
29
30
31
34
35
41
44
54
55
56
58
59
61
66
74
77
78
80
81
85
87
91
93
96
98
103
104
105
109
110
111
113
115
116
118
120
121
122
123
125
127
128
129
130
131
132
00:00
Computer programmingSoftwareElectric generatorFunction (mathematics)Device driverLoginAliasingCodeComputer programmingParallel computingCore dumpRight angleCoroutineScalar fieldInstance (computer science)Exception handlingFunctional programmingMultiplication signCodeGraph coloringDebuggerCuboidDirected graphLibrary (computing)SynchronizationTable (information)Surface of revolutionFormal languageProcess (computing)BitError messageServer (computing)Scheduling (computing)Computer animation
02:46
Exception handlingCodeComputer programmingRight angleParallel computing
03:13
Letterpress printingFunction (mathematics)Exception handlingCodeFunctional programmingComputer animation
03:40
Letterpress printingCountingRange (statistics)CoroutineRandom numberFunction (mathematics)Connected spaceEvent horizonRecursionBitException handlingCoroutineLoop (music)Functional programmingDirected graphThread (computing)outputCodeComputer programmingComputer animation
05:02
BitException handlingMultiplication signCoroutine
05:29
Letterpress printingCountingRange (statistics)CoroutineError messageException handlingControl flowBlogIRIS-TTask (computing)Data typeNetwork topologyCodeCoroutineRecursionFunctional programmingNuclear spaceBlock (periodic table)Network topologyBitRectangleReal-time operating systemCore dumpContext awarenessSingle-precision floating-point formatProteinReal numberCodeObject-oriented programmingMathematicsComputer programmingRight angleVideo game consoleSynchronizationComputer programming2 (number)Exception handlingRevision controlData managementCartesian coordinate systemGraph (mathematics)AbstractionMereologyRootMultiplication signInternetworkingSoftware frameworkDemosceneInheritance (object-oriented programming)KnotOrder (biology)Task (computing)Goodness of fitRouter (computing)FreewareDirected graphComputer animation
12:58
Game theoryBitRecursionRevision controlBit rateData conversionMultiplication signCoalitionFrequencyComputer animation
13:27
Letterpress printingTask (computing)TransportprotokollLoop (music)Event horizonLibrary (computing)Right angleBuildingTrailBlock (periodic table)Complex (psychology)SynchronizationComputer animation
14:23
Direct numerical simulationStrategy gameSequenceAngleMeta elementConnected spaceMultiplication signSynchronizationEvent horizonConcurrency (computer science)Regular graphMaxima and minimaComputer programmingContext awarenessBitException handlingNetwork socketRevision controlRight angleFunctional programmingData managementFormal languageOpen setSkeleton (computer programming)Process (computing)WordDirect numerical simulationComplex (psychology)Video game consoleImplementationDigital electronicsInstallation artStandard deviationCodeMultiplicationInternetworkingLibrary (computing)Modulare ProgrammierungCASE <Informatik>CoroutineBlock (periodic table)Address spaceVideo gameIP addressDomain nameImage resolutionNatural languageParallel computingDomain nameComputer animation
20:48
Artificial lifeFunctional programmingCodePlastikkarte
21:17
Event horizonTask (computing)Software testingHuman migrationStandard deviationQueue (abstract data type)Control flowDirected graphCodeUnicodeImplementationString (computer science)Game controllerLink (knot theory)Software testingLibrary (computing)Event horizonLoop (music)TrailNormal (geometry)CASE <Informatik>Entire functionHypothesisFile systemFunctional programmingModulare ProgrammierungInstance (computer science)Unit testingBlogLine (geometry)BitMultiplication signState of matterGoodness of fitoutputType theoryFunction (mathematics)MereologySoftware bugPoint (geometry)Synchronization1 (number)Musical ensembleComputer animation
Transcript: English(auto-generated)
00:06
Hi everyone Yeah, I'd like today to talk to you about trial and how it maybe can save your soul when you are working with asynchronous programming As you may know asynchronous programming is really not a new thing in Python It's been there basically forever like thanks to twisted
00:23
but since some years it has been something really bigger with this new async IO library and Especially the introduction of this async await keyword the thing is How how bad was it just before this async await keyword was was a thing
00:41
So let's see something so just in the beginning it was like that This is something that you no longer write in Python, so I had to find another language that still use it sometime So it's really horrible because you have to handle error by yourself. You cannot use the exception If you use a debugger on this basically you never know a callback how it has been created by who or something
01:06
So after some time people come up with a better idea So it's called the promise on JavaScript It's called a differ on twisted or future on an async IO, but it's always the same ID so now It's a bit better because you can you know
01:22
Combine promise together to do a synchronization between callbacks It's kind of callbacks on steroids, but you still you cannot use exception right and You know your debugger is still totally useless because it's still callbacks So after some time you've got a real revolution, which is a sink away keyword
01:43
So now we have this concept of a synchronous function, right? so it's much better because now you have the regular function and you have just a new kind of color of Functions the accuracy synchronous one and you can just use this away keyword and you stop the execution of this Function for some time and you give a chance for another coroutine to just write a bit
02:04
So yeah, it's much better. I think we can just improve it a bit more like this and Yeah, it would be perfect. Right? We're using this new async IO library. We're using the async away keyword. Everything's fine So this is the end, right? But yeah, maybe not I mean if you've been to the talk of URI this morning
02:24
You know that there is still kind of some trouble with async IO for instance Let's see this code. It's really simple like you see there is a scheduler function which act as a little server So it's a long running and from time to time. It will schedule a new coroutine, which does some job
02:42
The thing is each job is broken. It will raise an exception So if we run this, let's see what happens Yeah, I got the code right there. You can really we can see yeah big enough So, yeah if you're used to Asynchronous programming maybe you don't feel
03:03
It's that weird. But if you're not it seems really wrong there because there is an exception occurring here It's really obvious but and we never try to to catch the exception right in your code base There is no accept anything. But anyway, the the code was still running I mean we got an exception we just print it on the standard output and we go on like, okay. Nothing occurs
03:25
So yeah, it feels wrong. And the other thing is if we look at the stack trace It's not the stack trace is not complete We see this function raise the exception then it was this function which called the other one and we cannot go up so it's like
03:41
Here we created these new coroutines and in async IO when you create a coroutine you go like it's fire and forget You just create the new coroutine and then there is no connection between the creator and the creation And so it's really bad because it means when a one coroutine raised an exception it bubbles up up up until it goes to the The event loop and then the event loop doesn't know what to do with this exception. It cannot give it to anyone
04:06
so it just does the least worse thing you can do which would be printing it on the standard output and Just go finger crossed and say okay, maybe this thing was not too important and maybe we will be able to to go on
04:21
Let's see another example. So here it's a bit more complicated. I call it the recursive Russian roulette So it's basically we are playing Russian roulette, but we have with program. So this morning we were killing threats now We are just killing coroutines. So it's okay the idea is We have this function we let's say try our luck and if we get lucky
04:43
The coroutine doesn't blow off and what it does it just create two new coroutines that will just continue this same function And now we use this async IO gather So if you don't know what async IO gather does it basically just wait for the coroutine to finish
05:00
So if we run this code, what will happen? I Got it Here I think something wrong So if I if I just try it a bit to stop a bit faster Yeah, so we can see just we try our luck one time then we create new coroutines that will try
05:24
Eventually, we run out of luck here. And here there is the exception which is handled. We can see it's here and here it's getting crazy because Even if we are out of our recursive Russian roulette function There is anyway new coroutines which are created and which execute some code and create new coroutines and it's getting crazy
05:44
So what's happening there? If we think about the coroutine We have just our main coroutine first and then this coroutine will call the recursive Russian roulette function So let's say it gets lucky and it just has to create two new coroutines that Themselves get lucky and so create two new coroutines. So we're like this
06:04
If we consider this asyncio gather function, it's something like that. So you have the main coroutine, which is Listening on the coroutine one and coroutine two and we have coroutine one waiting on its children and coroutine two waiting on its children So now let's say this coroutine blows off
06:22
What's happening? And I saw it was what happening until I saw Yuri's talk and now I'm not sure of anything Which means asyncio is really hard even once you want to do a talk slightly about this so I saw that when a coroutine blow up and the coroutine is
06:41
Watched by asyncio gather. Asyncio gather will cancel the other coroutine It is tasked to watch and will make the exception bubbles up. So it bubbles up to the Coroutine two and this coroutine two is also watched by asyncio gather by the coroutine one by the sorry the coroutine main so we cancel coroutine one two and
07:02
We end up like this so now it seems a bit wrong because in fact the asyncio gather from Coroutine one couldn't kicks in so it was just killed. He couldn't destroy the sub coroutine It was tasked to watch and now we end up like this and given we just catch the exception in the main and
07:21
Just sitting around us for some time Then the coroutine the existing coroutine can just spawn new coroutines and go crazy So What's the problem here? I see three things that could be improved The first thing is it will be much better to have complex stack trace if we take one coroutine We should be able to read all the part of the coroutine and to be able to go up up up until we see
07:43
Okay, this coroutine has been creating there and if we go up enough We should go to see the the main of the of the function of the root of our program. Sorry The other thing which is a bit related to this if if we get an exception from Or coroutine it should bubbles up and never get silenced by something like, okay
08:02
We're just printing an STD out and we'll be enough It should if nobody catch the exception it should go up until you blow off our program I mean, that's how it works on the synchronous program, right? So it should be the same on asynchronous and finally something a bit slightly more abstract is We should be able to have our coroutine which which would have an easy way to connect our coroutine together in order to express
08:27
the lifetime of one coroutine According to another one, for example, we say okay. We have a parent. We have a child is apparent I want a child to die, too So yeah, it's time to talk to you about trial and you know this guy. I mean
08:41
his name is a Nathaniel Smith and He had these ideas is great idea. He said, okay There is new feature in Python like the async await and so what if we just drop all the the other deprecated way of doing a synchronous function like Promise and callback and we just focus on this async await thing and what if we go a bit further?
09:04
What if we invent new way new abstraction new building blocks to do a synchronous function at synchronous programming? And let's see how far we can go with this. And so we end up with trial like this So what's about trial there is three main concept about trial
09:21
The first thing is the async await keyword. We already talked about it. We already know why they are so great The two more exotic thing are the nursery and the cancel scope. So first the nursery here is a slightly modified Version of the recursive version roulette this way It's it was written for trial and basically the only thing that changes the red rectangle
09:46
The idea in trial is if you want to spawn new coroutine You cannot do it firing foggy style like you would do with a sink. I you must use a nursery object so putting that another way you have to say that every coroutine in your program will be connected to a nursery and
10:04
The good thing about nursery is there are asynchronous context manager So you use them with a sink with when you do a sink with a nursery you start when you enter it Does nothing but when you want to leave this block it will be blocking it will block until all the coroutines that are
10:21
Connected to this nursery will end So, how does it solve our problem? Let's see The coroutine we have before and now we said they are bundled inside the nursery, right? So it looks a bit something like that in fact, there is a tool in in trial, which is called the monitor and
10:41
This tool allows you to plug inside Trio application to just watch in real time the coroutines and so if you use this on the program You will see something like that So the idea is now what we have is a tree of coroutine It's free of graph and so it's much simpler
11:01
I think to visualize how trial works when you see this if we take our example back with the exception from here I think it's really simple to see that every time though your exception bubbles up into one coroutine up It's really easy for trial to know which coroutine it should close. So it goes like this and so it just works
11:22
It's really simple to see that you have a tree everything that under the tree Under the nodes that you are working on should be destroyed because this node is going to to be destroyed, too So you end up like this you end up clean Something else about trial is the cancel scope So the idea is if we are doing an asynchronous if we are using an asynchronous framework
11:43
It means that we are doing IO, right? The trouble with IO is it's basically you are waiting from some from someone else and this someone else can crash You can have the router and the other end of the internet which die or something like that So you always have to deal with with timeouts
12:01
So it will be it will be really great if we could really easily say, okay I want to have this part of the program which has this kind of timeout and to put it really easily But as we saw we already have this this tree of coroutine this graph, so it's really easy We've tried to just say okay. I want this part of the graph to run say in
12:21
0.3 seconds, for example So you to do that you just use a context manager So you you put a context manager with this cancel scope and you say okay this block of code I want it to run for at most this time and if it goes more than that Then we will just leave this block and every node that has been created. Remember we are we are like this
12:43
So everything that has been created under our coroutine you can destroy it. So you have the guarantee yet that no matter what if the Timeout occurs you you won't leak any coating everything will be clean Oh, sorry, I go too far. So yeah, one good thing about our
13:04
Recursive Russian retin right now is we got this timeout. So now maybe the game is a bit more fair We won't kill any caution. So I just written a trial version of This recursion Russian red and yeah, we run out of luck this way
13:21
Yeah, we're really unlucky. Yeah this time. We didn't kill anybody. So that's nice maybe you not bought yet to this concept, but Maybe you just have to think how you would have done to implement this timeout feature if you would have to Do this with just asking hi always a previous example
13:42
Okay, so That's it and that's one of the feature of trail actually it's there is really really few concept It's like an extremely simple asynchronous library. And that's something which is I think really really strange from asynchronous library because you know, you know twisted, you know asking guys are all with lots of
14:03
Documentation a lot of concept a lot of things and so it seems really complicated and we've tried You just read something and in half a day you're ready to work and you have those really small and simple building blocks But you can put them together to create really complex things the easy way I mean, it's easy to get things right. So I think it's a really great feature
14:23
Okay, so maybe you're wondering it looks great on the paper. But what about real life? So there is this use case. Let's say you want to connect to Debian org The thing is so you have this domain name you want to resolve it and you know internet is a big big thing with a
14:40
Lot of complexity so it's never simple So when you resolve your domain name, you will end up with multiple IP address And so now you have a new trouble, which is which IP address should I connect to? so the first idea you could have is Well, just try to connect to the first one and after some time if it failed Why don't we try the next one and so on so forth. The trouble is yeah, this is really slow
15:03
So there is maybe a better way. So the other way to do it is just to go a bit more violently So we just try everything in in concurrency and we take whichever is the fastest But it takes a lot more resources, right? So there may be a middle ground and So an attempt of this middle ground is a this thing called happy eyeball
15:25
so the idea is you start by connecting to the first IP address you have and If after some time you didn't I mean you still waiting for this connection to to succeed then you try another address and if this other address just failed
15:42
faster than the timeout then you try the next one and Eventually, one of them will succeed and then you you can cancel all the other coroutine because you have a new Connection which is correct now So how complicated is it to implement? There is no implementation of this in AsyncIO, but there is one in Twisted
16:05
So this is a code. I Didn't read it myself, but according to Nathaniel. It's quite complicated code There is a lot of nested function inside them. So it's hard to read. It's hard to understand it had to it's hard to manage So the Twisted guy they are aware of this and so they come up with a new version of this
16:25
Which is much better, which is no simpler Okay, they said it's it's less crazy. It's easier to work with but they are still not happy about it But the thing important here is the people working on this are like top guy
16:40
I mean the guy who written this this implementation. The second one is the creator of Twisted Installs So it's basically the guy which has the more experience in asynchronous programming of the whole Python community So the problem is not the people the problem is the language the language. I mean, we don't speak the right word It was really easy talking human language to say, okay, that's how happy eyeball works
17:03
But when you want to write it in Python, it gets really hard and this is not what Python is about Python is about being Able to write complex thing easily. So maybe it would be better in trial. Who knows? This is a skeleton of our function. So we call it open TCP socket
17:21
It takes our hostname and the maximum time we want to wait between two attempt of connection So the first thing is a really simple. We just to do the DNS resolution and then we got multiple targets We could try to connect it to Here we use this trial socket module Which is just the same thing than the regular socket module the standards one. It's just an asynchronous version of it
17:47
After that, we define this winning socket So this will be the variable which eventually will get which socket one which socket is ready to be used And if we have no one well, we just raise an exception, right? Now what was what we what do we want to do?
18:01
We want to do multiple thing at the time We want to start a connection and then another one and this kind of thing So to do this in trials, there is not two ways only one way which is we just have to create a nursery So yeah, that's what we do. We create the nursery. We just use it as an Ensynchronous context manager and we create this attend function
18:21
So every time we try a new attempt against a new IP address We will call this attempt function. And so we we start by calling it for the first attempt So, yeah, what are we going to write inside this attempt? What we can see here is except for the first attempt we are always waiting The first thing we do is we just waiting we're waiting for two things. In fact, the first thing is
18:45
if the previous attempt is taking more than the timeout time and The second thing we are waiting for is if the previous attempt just failed fast So to do this in trial what we can do is we can create Multiple events each attempt will have a failed event. So every time it failed it will set up this event
19:06
So now we can wait on this event and we use Cancel scope to say okay, I want to wait on this event But I want I don't want to wait on this event no longer than this time So if we reach the timeout, we'll just leave this context manager and continue a code
19:22
So now you we are almost down to do the actual job But just before that we have to spawn the next attempt because before doing our own attempt We have to spawn the next one because it will watch to see if we are taking too long So to do that, we just use the nursery So we just take the nursery and ask the nursery to create a new coroutine and to execute this next attempt on
19:46
the next IP address And now we're all set. We can just do our socket connection, our socket trial connection So again, it's just like the the regular the regular socket module library
20:02
So now there is only two possible outcomes, which are first The The connection failed. So what we do is just we we update this event about okay We couldn't do anything. So now it's up to the next attempt to Try to succeed and the other outcome is well, we got the winning socket
20:22
So we just have to update this and now we can cancel the nursery So the idea about cancelling the nursery is like we cancel all the coroutines which are connecting to this nursery and given we have to cancel all those coroutines It means the nursery is now free and so these blocks we had there with this asynchronous context manager
20:42
We will just leave it automatically and continue the code. So yeah now we're done Maybe you're not sure if it works. So I have the code here. I think it's this one Yeah, so it's just the same. I show you just I edit this main function to just say, okay We'll try some Debian.org
21:01
Obviously, it would be a much more interesting if I have done live coding but No, it's not my kind of style. So let's just pretend It works Anyway Yeah, thank you
21:24
I'm doing applause too because it's not my code. It's Nathaniel's ones. I stole most of my talk from his own. So yeah Anyway, so what does Trio offers you? There is basically everything that you can expect from an asynchronous library like, you know, all the standard stuff
21:41
You can use asynchronous file system access. You can use networking There is all those synchronous tools like we use event you have Q you have locks, etc There is a really good testing helper if you love a pytest There is a great pytest module if you have a hypothesis, there is a great hypothesis module If you don't love hypothesis, you should try hypothesis and then you will love it
22:03
There is this control C working. I mean, it's it's feel like yeah, what is this? I use control C it works But no Control C is really hard to get it right and nobody knows about it until you have read this article That nothing else wrote on his blog. You should definitely go to it, but everything is really interesting
22:21
So yeah, this is working And finally there is one of my favorite feature is this compatible layer you know, I think I owe is really great because It has it. It makes all the entire asynchronous world in Python compatible with each other So with a sink I owe you have compatibility with twisted you have compatibility with tornado. Everything is compatible
22:43
So now we try you just have to write And I think I owe loop but you write it inside in trio and now you get compatibility with the entire rest of the Ecosystem of the asynchronous ecosystem just this way So it's really great because it means that your own code base the code
23:01
Do you just want to type fast and want to do the thing you can use it in trial? So you will get safety But for the third-party library the thing that like say you want to connect you possibly you want to use it? I think PG because it's really great But this code is already well tested so you know, there won't be any trouble and you can really easily plug this
23:22
Asynchronous library with your code which is written in trail. So yeah, it's really great and Yeah, I'm not the only one who think this code is very great. There is plenty of famous people like even rock stars Yeah, so anyway, I guess this is about right one more thing I just I just stolen
23:43
Maybe half of my Conference is from a national conference from last Python and the link which is here is is home so you should definitely check this out because There is a lot of things if you're interested in this topic, it's like okay, you should watch this conference You should read this blog post and this one and this one
24:01
So if you want to become better at this and you don't know already This guy and that annual you should definitely go to this link Yeah, that's it
24:26
Hi, that was excellent. Could you say a little bit more about hyper test and indeed whether unit test is also available with this thing or
24:44
Yeah, so what do you want to know about type of this? You already know this thing or no Okay, so it's basically the greatest thing if you want to test code The if you want to test code Anything anything the idea is
25:00
Normally, what you do is you create use cases. So you just say okay. I want to test this function I will put this input and I am I want to get this output But the thing is most of the time when you do this you forget things for instance Let's say you want to have a function that works on the on the strings You will try simple case like you uses ASCII strings
25:21
But you will forget that there is like unicode strings and there is unicode string with Unicode cut points which are not printable this kind of thing. So it gets really really tricky really complicated. And so we've Hypothesis what you can say is Yeah, sorry, it's my accent which is Sorry, I'm French
25:45
Yeah, so now you can just turn around and say to people how great hypothesis is and pronounce it, right Yeah
26:01
Yeah, that's it so anyone who want to talks about trio and not hypothesis and French accent Yeah, you have to come here
26:36
No, I didn't encounter any bugs yet there is little kind of let's say quirks sometimes
26:45
Because yeah, obviously you're using one and synchronous library with another one. So obviously you have to be careful, but Yeah, if you're careful enough, it's really straightforward
27:00
And yeah, by the way, try a code base is Crazy good. I mean when you read the code base there is like two times more comments than the code and every time it's like the guy is is writing all the State of the art about just this line of code this kind of thing We are doing is this is this this and if you want to know more about this, you should go there there there there
27:24
So just if you don't want to use trial at least read the code
28:01
Now it's you should see it a bit like you you're writing some code in a sink IO and Say you say okay. I want to go faster. So now I will don't use the async IO normal implementation now I will use UV loop. So it's still a sink IO, but it's just another implementation of a sink
28:20
Right, so now it's just the same thing with trial with trial you got an implementation of a sink IO which is made with trial so you can run totally just asking a code with this but it didn't be Really interesting and so the good thing is you can have a part of your code Which is in trial and the rest which is in async IO and you use this Implementation of the async IO event loop to make the both be able to talk to each other
28:50
Yeah, but twisted and tornado. I mean, I'm not 100% sure about this because I never tried it But what they say it twisted is now compatible with the async IO event loop. So you have no more no trouble now
29:09
Yeah, Jeevan Yeah, sorry, you cannot have compatibility with PHP 2
29:20
Now you're right