 Hei, gael i. Rwy'n dech Соeth, rwy'n gydag i'n fflawni'r cwp, a rwy'n gydag i'n fflawni'r Eysincoio Alternative to Flask, Ond i'n gweithio, rwy'n gwybod i'n ffrindig i fflawni'r eysig o'r cwp, a'r ddych chi'n gwybod i'n fflawni'r Eysincoio. Rwy'n gydag i'n gwybod i phallu'r peth o'r dyfodol. Rwy'n gydag, rwy'n gydag i'n fflawni'r cwp i'r ASGI, o'r ASCII alternative to Flask. I suspect this is more unfamiliar. In fact, can I ask if people know what SGI is? Oh, that's actually pretty good. I was thinking there would be very few people. Great, okay. Well, I maybe don't need to say this either. But I'll try and introduce what ASCII is as well. But before I get to that, I just thought I'd say a little bit about myself. Cywgwyr for markets. You may have heard me introduce markets yesterday. You can find me as P.G. Jones on GitHub and GitLab, but I don't have a Twitter, sadly. And you can, I use this avatar so you can kind of identify me from this. And there's a bit of an interest. I used to be a particle physicist working down this mine in Canada, so that's what the avatar shows. So anyway, onto the thing that's really interesting. So I think to introduce Cawr as an alternative to Flask, I should probably just introduce Flask and make sure we all know what it is. I suspect you do. But this is kind of the canonical quick start example for Flask. I think it really shows off just how nice an API Flask is. And you see here I have a simple root, which is the root path. And any request that goes to that root is going to get this rendered template, the index that HTML back. And this is essentially all Flask is. It's a micro framework. It mostly requests to the root handler functions and then formats and returns the responses. If you want to do anything more than that, there are no batteries included like Django. You have to go out into the ecosystem and look at the various extensions. And Flask has a really good ecosystem. So you're going to find extensions for the stuff you want to do. And yeah, this is pretty much it. It's good to just, there's not much to it, but to remember it because I'll show you the court version in a bit. The last thing I should say about Flask is it's a very mature project now. It actually reached version one this year. I think it's very stable, very heavily used. Okay. So to start to talk about ASCII, I'm actually going to start talking about WISCII or WSGI. And WSGI is the kind of specification on the protocol, if you will, that allows the server to be separated from the framework and allows you to choose different servers depending on what you want to optimize for. And the reason particularly for Flask you want to do this is that if you expose Flask to the outside world, it's not going to scale particularly well and it's going to be quite easy to attack and bring down. So what you typically use is a WISCII server. I personally like Geunicorn, which I combine with a VentNet, which means that it's now going to be asynchronous and concurrent. And I use that with Flask. And that's going to scale nicely. And it's going to be much harder to bring down. For alternatives, you can use it like new WISCII and you can use various different asynchronous workers as well with Geunicorn. So that's what WISCII allows. And WISCII, if you don't know, it kind of boils down just to this really. It's a single callable, your application. And when you call it, the WISCII server will pass it in Viren, which basically is a dict of everything about the request. It also contains information about the environment that the request is working in or the application is running in. So like the environment variables, the route path, that kind of thing. And it passes it a callable, which can be called to start the response to the client. So a WISCII server will get a request from a client. We'll call this application callable and then wait for the start response to be called. And then this application will return the body of the response. That's basically what it is. It's a synchronous system to respond to HP requests mostly. And it's proved really well. I think it came out early 2000s. There's lots of frameworks based on it and there's lots of servers based on it. And I think as a user in the Python community, you've got a lot of choice because of this. And it's pretty good. So now I can tell you what ASDI or ASCII is. And it's the same principle. Basically you have an ASDI framework, in this case Quartz, and you can use various ASDI servers or ASCII servers. And it's the same idea. You split the framework from the server and as a user you get your choice of what framework features are important to you and what server features are important to you. And that's the basic idea. ASDI itself stands for now the asynchronous server gateway interface rather than I think it was the web server gateway interface which is whisky. And it's a bit more complicated but the principle is still exactly the same basically. So your application now is a double callable. So the server will call it once and pass it something that's called the scope. And the scope is very similar in principle to the environment. It tells you everything about the request and the kind of environment you're working in. And so you get the scope and it calls it again in an asynchronous way. So it awaits it effectively. And this call is gets past two additional callables, a receive and a send. And these are both asynchronous as well so you have to await them. So now as you receive the request, of course the request body doesn't all come at once. You have to await it in chunks which is helpful if you want to stream a request body. So your event, you await the receive and then you send stuff back to the server which it sends to the client which again you await. So this is fully asynchronous. These two functions are likely going to do IO. So when they're doing it, your event loop is going to go do something else. And this, if all the asynchronous web frameworks that exist adopt this, will allow you to choose whichever framework you want and whichever server you want based on that. And this is how court is now. Court is now an ASCII framework. So hopefully it's that simple and you've now got a good idea what ASCII is and how it comes about. I think it's a very legitimate equivalent to whiskey in the asynchronous age and could even perhaps, because asynchronous coding makes a lot of sense for web servers, could even come to replace it in the future. So the last thing I wanted to say right now about ASCII is that you have a bit of choice already, which is kind of nice. There are three ASCII servers that I know about. There's HyperCon, UVCon, and Daphne. And HyperCon and Daphne both support HTTP2 straight away. You really don't really need to do anything. They just do HTTP2. For the most part, you can't even tell in your framework whether it's HTTP2 or not. HyperCon goes a bit further and also supports server push, which I'll talk about later and also allows you to return a custom response to the WebSocket upgrade request, which could be useful if say you want to authenticate that route and return four ones. There are some extra differences. So Daphne is, I think, is very heavily used in the Django channels community. And I believe it's quite robust because of that heavy use. UVCon is very quick, as far as I can tell. It also has by far the best logo, which I decided to put up. And HyperCon was originally part of Quart when I split it into ASCII framework and server. And HyperCon, I think, is the most complete feature set of, I think, things that are useful. So that's basically your choice of what you want to do. So those are the, that's the ASCII part. I'm going to come on to Quart now, but to motivate why Quart exists, I just wanted to bring this up again. And it's something Dave Beasley, I think, brought up in his talk. And the basic concept is that it's quite easy to run or to get asynchronous code to be called from an asynchronous code routine. You just await it. It's also very easy to call synchronous code from an asynchronous code routine. You just call it. It gets very hard when you try and do it the other way around. When you have a synchronous function and you're trying to call another code routine, it's going to work because you're calling a code routine function. But all it's going to do is create a code routine and it's just going to sit there and do nothing. So it could be quite misleading and take you by surprise at the start. It doesn't error or anything like that. So when you first come across this, the next thing you'll inevitably try and do is then await it, which is going to be a syntax error. And this is... I think the problem really forces these additional libraries that now exist that are just async compared with the existing ones that were sync. And it's the motivation for CORT. I started by looking into Flask to try and see if I could bring asynchronous stuff into it. I couldn't... And that's why CORT exists. So to introduce CORT, you can find it on GitLab under my namespace. The current release is 0.64, which I released about a week ago. It's got a nice logo. Someone at Coder, Coder, sorry, made for me, which is very nice. It's MIT licensed and it requires pretty much the latest version of Python. And I might upgrade it because there's some nice features in Free7 I want to use. It's been in development for a little over a year now. But the real thing that I think makes CORT interesting is that you can take your existing Flask code and the API, the framework API is exactly the same. So all you need to learn and worry about is the asynchronous parts. And if you take that original example, you can just find and replace Flask with CORT and add the async and await words where appropriate. And this could take a bit of getting used to, but basically you want to await any function or any call that you think might do some kind of IO. And that's basically the rule for it. So just to emphasise, the thing I think makes CORT useful is you can just find, replace Flask with CORT, add the async and await, and that's your transition. You just need to learn the asynchronous parts, not the framework. And to emphasise how I'm doing this, like CORT's aim is to exactly match the Flask public API which includes the Verxerc public API in a lot of places if you know about how Flask works. And it tries to match the Flask private API, especially the bits that are well used. And so that's how they relate. I'll come back to later on how they could relate into the future as well. Another example of just to emphasise this point, if I took this little snippet off the Flask snippet sites, it's just a basic authentication system. And all I had to do to make this work with CORT is just add the async keyword and the await keyword. And I'm done. Hey. So having said all that and hopefully convinced you how easy it is, and I have to tell you how hard it is as well. And it's a bit of misleading. So if you imagine now that you want to do something serious in Flask, it's not batteries included, you're going to want to reach out to some extension, probably some kind of database thing or maybe CORT's or something like that. And your extension code at some point will probably do something like this. It will call a function that is in fact a coroutine function. And in CORT, the request.getJSON is a coroutine function. So much like earlier, this data is going to be a coroutine. It's not going to have a key index. You're going to get an error. And so the extension is not going to work. If you were to have this extension work with CORT, you'd obviously need to have it be an asynchronous function and await it. Now, there is a way around this with monkey patching, which I can talk about later. But monkey patching has its concerns. So now you know the caveats of it. I'm going to tell you some of the things you could do beyond Flask now that you're really asynchronous and the framework understands this idea of asynchronous, being asynchronous. And the first thing I'm going to talk about, which I think might come to whiskey soon and possibly in Flask, or probably in Flask as it does, is that you can stream a request. So if you're sending to a server something that has a very large body, you can now stream that and your server can do stuff with that data as and when it arrives, which could be useful for streaming. And to do it in CORT, all you need to do is asynchronously loop over the request body. That's it. So I've added a timeout as well because this is a very easy way to attack servers simply that you keep promising to send data that you never do until you exhaust all the connections. So that's why you want the timeout, but that's it. And I think this is one of the things that Python has quite nicely and that it's quite an expressive way and quite clear way for this to work. So, of course, if you can stream requests, you can stream the response as well. And as of 3.6, you have these asynchronous generators of which the send events is. So in this case, in the ellipsis, you can imagine that I'm getting data from, say, a Redis Pub sub or maybe some other place. And then I'm turning it into something called a server-cent event, which if you don't know is a way that browsers can just hold open a connection to a server and just get events sent to it. It's very useful for notifications or maybe news and stuff like that. And then I yield it and I yield the raw bytes and, of course, going to send that to the client. And with the right headers to tell the browser what's going on, i.e. that it's an event stream, this will work as you think it would. And again, I think the asynchronous generators makes this quite a nice and clear bit of code. Going further, again, HTTP2 is no longer like HTTP1, which is fairly synchronous. It is asynchronous. You'll get many requests to go over the same connection. It will pipeline them. And so HTTP2 fits really nicely with asynchronous coding. And the ellipsis here are just to demonstrate that you can use HTTP2 with Quort without even needing to think about it. It will just do it if the ASCII server does it. There's nothing for you to need to do. Quort also allows you to go a bit further and I mentioned briefly earlier the idea of push promises or server push. So to explain how that works, if I'm a browser and I make a request for an indexed HTML file, when I receive that file and I pass it, I'm probably going to find out that I now need to go and ask for some CSS for some JavaScript. So HTTP1, I now go and make those requests. And HTTP1, I think I can make about five of these before they have to complete and I can make the next ones. With HTTP2, as a server, I know that the client is going to ask for these. So I can send these files to the client before it knows it needs to ask for them. And this is what a server pushes. So in this case, for space, I said that the indexed HTML is going to ask for the CSS file and so the server is going to push it to the client and so when the client knows it needs to get it, it's already got it. And you can make the time to first load of your page much quicker this way. I actually haven't seen this used much in practice, so I think it's quite interesting, but there we are. And I'll add here that I've written some blog posts which you can go and visit to find out more details about how this works if you're so interested. Carrying on, beyond Flass, there's WebSockets. Of course, Flass can do WebSockets as well, but it's usually done by using Gevent and you have Gevent workers going on. It's a very simple principle. But in Quart, it's asynchronous, it's just built straight in. And what I've tried to do with the API is make the WebSocket API the exact analogue of the request API. So you can get the headers of the request from the WebSocket object. You can get the query strings, cookies, all that kind of stuff as you normally be able to get. Also, if you know Flass, the stuff like the before request and after request, and you get the before WebSocket and after WebSocket analogs. But the very simple thing to show for an echo server is just to have a while loop. Every time the client sends you some data, you just send it back. And again, there's a blog post if you want to read more about how you can do more advanced patterns. Because of course this one here, you'll need the client to send you something before you can do anything, which may not be what you want. At this conference though, if you saw the WebSockets talk, which is very good, you of course know that WebSockets are used for tomatoes and cats. And so this is, I had to take out some of it because I couldn't fit it on the slide and be readable. But this is how you might do something like the tomatoes and cats in court. And this is the talk. If you want to see it, it was really good. And if the author's here and wants to see the rest of the code, then I'll be happy to talk to them. Okay. So those are all some kind of snippets about how you can go beyond Flass with Court. I just emphasised you can do everything you can do with Flass with Court. Anyway, so this is just extra. The other parts that you'd like to do with Flass really come from the extensions. And I'm not going to go into details about it, but Court has a system built into it that will monkey patch the existence of Flassk. So a lot of the Flassk extensions you can use in Court. And if you visit this website, you can find out which of Flassk extensions will work with Court if you use this system. There are two pretty common ones, though, that don't work with Court, that now there exists some extensions to do instead. One is FlassCourse, which is a CourtCourse equivalent. And one is, well, pretty much all the restful Flassk extensions do not work with Court. So there's this Court Open API, which has been developed to replace that. All right. So I'm jumping around a bit, but I'm trying to tell you about different parts of Court. So Court is a fully type-inted website, framework, sorry. So every part of Court is type-inted, and you can use your tools, my pie, for example, against Court to figure out if you're doing the right thing. And I think one of the places where this is really helpful and something I tripped over a lot with FlassCourse is knowing what you can return from a root handler. Because there's quite a lot of different combinations, and depending on the combination, it will interpret it a different way. So for most people, you return some string with maybe an integer, which will be the status code, then maybe a dictionary, which will be your headers. You could return a synchronous generator or a normal generator, all the whole response objects. You could then combine the response with a dictionary, which overwrites the headers or I think adds the headers. And so all of this, I think, helps if there are type-intings there. I think it's one of the great features that Python has added. Okay. So those are all the reasons why I think you should use Court, but everyone always asks about benchmarking. So I thought I should talk about benchmarking. And so this is a really simple one where you have a root that just returns hello. It's a hello world. It's a get request. And on my machine, if I set this up, I can get about 500 requests a second with FlassC. If I use the Daphne ASCII server, I can roughly double that. And also I can roughly double it if I use EventNet with FlassC. If I use Quart on its own, I can do slightly better. I can get about 1400. If I use Quart with HyperCorn and UV Loop, I can do better still, about 2,500. So about five times FlassC alone. And I should also mention that Quart by default is HyperCorn, but without UV Loop. But if I use UVCorn, which is the fastest ASCII server, then I can do even better still. I can do about 3,500. That's not a very good benchmark, though, because nobody actually has a simple web server that returns hello in production, I think. Instead, you're much more likely to have something like a CRUD app that talks to a database. And so what I've compared here is FlassC with Geunicorn, EventNet, and PsychoPG against Quart with HyperCorn, UV Loop, and ASyncPG. And ASyncPG is very quick. UV Loop is very quick. They're the same people. I think Yuri spoke about them. And you can see quite a difference here. It's a factor of free, roughly speaking. And this is quite nice. To me, it's an additional thing. It's the ASync features I want, and this is just a bonus. I should probably update this as well, because with UVCorn, it will probably be quicker still. So to talk about the future, I've started talking with the FlassC authors. It would be great if the two could merge in some way, and we try and get ASync stuff directly in FlassC. It also has been suggested that Quart and HyperCorn support Trio or Curio, much like Geunicorn supports EventNet and Gevent. And also I'm open to suggestions if you want to make them. So I'll conclude by hopefully I've convinced you that ASCII is an ASync equivalent of WISCII and very good at it. And hopefully you think Quart is useful. So if you want to suggest any contributions or bug reports or anything like that, that'd be great. So thank you. I think there's a few minutes for questions if anyone would like to ask a question. I'm sure you were expecting this question, but, well, first let's talk. What about Sonic compared to Quart? Because it's somehow the same approach trying to be more or less compatible. Did you try it? Because it was not to be in the MeritMark and I'm curious. So Sonic is, in the ASCII sense, both the ASCII server and framework, it does it all effectively. And it doesn't match the Flask API, but it's similar too. So you still have to learn a new API. And benchmarking wise it's much quicker than Quart, even with UV-corn as your ASCII server. But there are lots of reasons why there is. So you start, to make Quart as fast, you can start by disabling some of the attack protection. So there was an article part on Reddit about how you can bring down Sonic servers fairly easily. You can't do the same with Quart. But if you turn them off, you get some performance back. In fact, I tried to build a prototype in Sonic of my current service in Flask, and I don't know why, but it becomes slower. I don't know what I did wrong. It's probably something. So it's quite easy to get Quart out with Async doing blocking IO, so maybe? Maybe it was that. I used the same page, I used the same Redis, but I don't know what they did wrong, but it was slower than. OK, so the results were comparable. So I could compare it to API results and were the same, but it was slower. I don't know why. OK. Yeah, usually I found Sonic is quicker. I will take one. Thank you. OK. I have great talk. Since you mentioned also considering to move it more together with Flask, I just want to let you know some work being done on making all the work like more Async friendly, such as making request parser to not do IO on its own, but some ways apparently working better with Async. So it might be interesting to look into maybe even contributing back to those parts. Yeah, definitely. I guess eventually it would make sense to have since Flask and works like one to support Async anyway. Yeah, I think one of the things I've seen in the form parser is to make it Sanzio. Yes, Sanzio is at the current pull request diamond actually. Yeah, so that's pretty ace that is because then you can use it however. The HB parser that HyperCorn uses is H11 and I think everyone uses HyperH2 for HB2. Both of those are Sanzio. So yeah, that really helps. It would for sure be interesting to have your parser around maybe in the Flask IRC channel, Poco on Freenote for those things. Because sometimes our discussions related to it and it would probably be interesting. Yeah, thanks. Hi, I have two questions. First off, I saw that the Quart app was launched with app.run. So is there a way to run the Quart app without having Quart's drive the event loop? Yes, so normally you'd use an ASCII server instead. So HyperCorn. If you want to directly control the event loop, you can pass the event loop to HyperCorn and say use this one and it will do that. If that's what you're asking. If you really want to do it yourself, then you can just use the ASCII interface and write your own ASCII server effectively. Okay. The other one was about HTTP2. Do you know of any front-end web servers that support reverse-proxying HTTP2? So I think Nginx does that. No, they explicitly do not. Oh, okay, then I don't. Okay, because I was wondering, do you want to run this Quart directly on the internet? I think hopefully with the ASCII server you can, yes. So yeah, I do that myself. I thought Nginx did it, but I'm being misled by it. At least last time already, it did not. Okay. Okay, thank you. Thanks. So I think there's time for one more. All right. So Flask has the test client. Does Quart also have it? Yeah, it's pretty much the same. Is it a sync or a synchronous client? I'm trying to see. It is a sync. I'll try to see if I can show you. We've got a few seconds. If there's internet, what it looks like, because I haven't got a slide. But yeah, it's pretty much exactly the same. So there you go. So you create the test client, and then you just do a wait to get, sort of just a normal get. All right, cool. Another question, maybe a bit related. Are there any async over a remise already, like SQL Alchemy? There are. So there's the one I think I like the most, but I haven't used it so much is Gino, which declares that it's not an RRM, which I suppose a bit misleading. And there's a few others. I think Peewee has an async version, and there's one more whose name I've forgotten, but it's not clear yet which one. I'm not sure which one's the one I would choose in production. All right. I tend to just use async PG and SQL statements. All right. Thank you. Okay. Thank you. I think we're out of time.