Monitor performance issues & errors in your code

#452: Top Quart (async Flask) Extensions Transcript

Recorded on Wednesday, Jan 10, 2024.

00:00 Have you heard of Quart? It's the fully async version of Flask created by Philip Jones,

00:05 who's working closely with the Flask team on these parallel projects. The TLDR version is that if you

00:11 want to take advantage of async and await and you're using Flask, you want to give Quart a

00:15 solid look. We've spoken to Philip previously about Quart. This time around, he's here to

00:21 share his top Quart extensions and libraries that you can adopt today. This is Talk Python to Me,

00:27 episode 452, recorded January 10th, 2024.

00:31 Welcome to Talk Python to Me, a weekly podcast on Python. This is your host, Michael Kennedy.

00:50 Follow me on Mastodon, where I'm @mkennedy, and follow the podcast using @talkpython,

00:55 both on mastodon.org. Keep up with the show and listen to over seven years of past episodes at

01:01 talkpython.fm. We've started streaming most of our episodes live on YouTube. Subscribe to our

01:07 YouTube channel over at talkpython.fm/youtube to get notified about upcoming shows and be part of

01:13 that episode. This episode is sponsored by Posit Connect from the makers of Shiny. Publish, share,

01:19 and deploy all of your data projects that you're creating using Python. Streamlet, Dash, Shiny,

01:25 Bokeh, FastAPI, Flask, Quattro, Reports, Dashboards, and APIs. Posit Connect supports

01:31 all of them. Try Posit Connect for free by going to talkpython.fm/posit, P-O-S-I-T.

01:37 And it's also brought to you by us over at Talk Python Training. Did you know that we have over

01:43 250 hours of Python courses? Yeah, that's right. Check them out at talkpython.fm/courses.

01:50 Philip, welcome back to Talk Python to Me. Great to have you here.

01:53 Thank you. Thank you for the invite.

01:54 We're going to talk about web things. I love, I love some web things. We're going to talk

01:58 about async. I love async. And we're going to talk about a lot of little cool things you can

02:03 plug into your web apps that make them more awesome. And we give a lot of people a lot of

02:08 things to think about. So yeah, let's, let's do it. So I guess quickly, before we get into talking

02:15 about Cort, maybe a little catch up on where it's been since the last time we spoke about that.

02:19 Give folks a bit of background on yourself for who don't know you.

02:23 Sure. Yeah. I guess probably in this context and maybe the people listening are probably known

02:30 for Cort and Hypercorn and helping maintain the pallets libraries like Flask and Werkzeug.

02:35 But well, outside of that, I guess in my professional career, I work as a

02:40 software engineer in London. I work for a company called Curaleaf International. And

02:45 quite interestingly, because it's not very well known, we're one of the few companies that do medical cannabis in the UK. Not many people know it's legal.

02:52 Yeah, I'm pretty.

02:53 Putting that aside, so I've worked there for about a year and a bit now. Before that,

02:58 worked at various other software firms. And then before that, I was actually a particle physicist

03:02 working on experimental in Canada in a deep mine, which was quite a good fun.

03:05 So you like your variety of your jobs, I can tell.

03:09 Yeah, I had a definite career shift.

03:11 Indeed. Just a funny sidebar, you know, cannabis was legalized here three or four years ago in

03:16 Oregon. There are more cannabis shops than there are Starbucks, than there are McDonald's. Like,

03:22 I could walk to four from my house in five minutes. It's nuts. Anyway, it's very different

03:29 than it was a few years ago, for sure. It's all fine, but just different. All right. So

03:33 Cort, like Flask, kind of, but very interesting. It's got some other capabilities. Tell people

03:40 about Cort. How's it relate to Flask? There's a lot of crossover. You and David Lord are working

03:46 pretty closely, it seems like, on things these days.

03:48 I started Cort about, I guess, seven, maybe a bit more years ago now. And the original idea was

03:54 make Flask async. At the time, I couldn't figure out how to adapt Flask at all in that direction.

04:00 So I thought, okay, I'll start by just re-implementing the Flask API using a

04:04 Async Await. And that's what Cort started out to be. Over the years, David and I have worked

04:10 to try and merge the two together. So the very latest change that's happened a few months ago

04:15 is we actually based Cort on Flask now. So there's a lot of shared code now. And for a long time,

04:21 Cort was based on Werkzeug. And if you're familiar with the Palettes and Flask ecosystem, Flask is

04:26 built on Werkzeug. And Cort was, but now Cort is built on Flask, which is built on Werkzeug. And

04:30 they're very, very similar. Yeah, that's fantastic. Because the more that you can share,

04:34 like the more you all can just focus on features and making it better rather than

04:39 duplicating that effort. The other aim is that the APIs, well, the same as they can be,

04:44 other than async await. So you can take your Flask code base and move to Cort just by

04:48 adding async and await. That's the dream, even closer than it's ever been to that now.

04:51 You're pretty close to be able to rename the word Flask, keeping the case sensitivity intact from

04:57 Flask to Cort. And it works, right?

04:59 Yeah, in simple projects. Yeah. The more complicated ones, it comes down to the

05:03 extensions, which we're going to talk about.

05:05 Which is, yeah, that's the whole topic of today, right?

05:08 Yes, exactly. But yeah, that's the aim. Our long-term aim is to actually merge the two

05:13 and be able to say async Flask. Well, it's kind of the case now where you can say async Flask is

05:18 Cort and we can't quite do what Django has done and put async Django as it is in Django. Ours,

05:24 you have to make a naming choice from the start. But yeah, I think it's getting as close as we can

05:29 get. Maybe we'll figure out how to merge them in the future.

05:31 Yeah, it'd be great if it just, you could choose to use async def or just regular functions for

05:37 your views in Flask and not really think about it.

05:39 You can do that now.

05:40 Okay.

05:40 We've been able to do that for about two years, I think, trying to figure out when we merge that.

05:44 So you could use either, it's just not as performant. If your code base is going to be

05:48 mostly async, you probably want to switch to Cort fully because it'd be more performant

05:52 and more reliable and robust.

05:54 Sure. Why is that?

05:55 The Flask implementation will run your async code on a separate thread. So you get all that

06:00 overhead of switching between the threads, sending the information over and back, et cetera.

06:04 Right. Does it run like an async IO event loop on the other thread, but then

06:09 we basically curry the results back and forth?

06:12 Yep.

06:13 And with Cort, it's running right on top of an ASCII server straight, right?

06:18 Yep.

06:19 Yeah. That's super cool. And you might be responsible for one of the more prominent

06:23 ones out there. Tell people about what is an ASCII server for people who don't know,

06:28 why does it exist? And then the one that you got.

06:30 Yeah, sure. So historically there was the Whiskey Standard, which separated servers from

06:35 frameworks applications and gave you a choice between how do you want to serve and deal with

06:40 concurrency and stuff? And then how do you want your API to build your apps, the framework to be?

06:45 I think it was a great choice because it made the ecosystem really, really nice for developers.

06:50 And ASCII is the same thing for async. And so Cort is an example of an ASCII framework

06:56 and Hypercon is an ASCII server. They originally together, much like Sanic started out as well,

07:02 but then I split that out Hypercon into its own thing. And then later on now Hypercon is also a

07:07 WSGI server. So yeah, in some respects, Hypercon is just a general purpose server for Python.

07:12 And so Hypercon, it lives in the same general usage basis, G-Unicorn and MicroWSGI, UWSGI.

07:22 Right. So I could run it in production in a kind of like a overseer mode where you can fan out

07:27 worker processes and system daemon, system D all that. That where it's at?

07:32 Yeah, it's exactly like that. Yeah. And it also in the ASCII space, it's similar to UVicorn or

07:37 Daphne. That's it. I think there's another, but I've forgotten the name just now.

07:41 Yeah. I was just messing with some Docker stuff and I was running the web app, a FastAPI app,

07:49 G-Unicorn using UVicorn workers. So handling the ASGI stuff that way. Is it better to use

07:56 Hypercon rather than something along those lines? A little more direct maybe? Or what do you think?

08:01 I think it's a bit of a choice. So how it differs from UV, well, UVicorn in general is quicker

08:06 because it's HTTP parser. HTTP 1 parser is quicker. And there's a few other choices that

08:11 tend to make it a bit quicker. However, this last time I checked, UVicorn doesn't support

08:17 as many extensions as Hypercon and it doesn't support HTTP 2 or HTTP 3. Now, most people don't

08:22 actually serve that directly. They tend to have a load balancer in front, but I tend to do that

08:26 because it's quite exciting. So you're getting there with Hypercon. And then after that, it's a

08:29 bit of a choice between what features do they both have, what do you want to use? So they're fairly

08:35 similar, I would say. Sure. Okay. Somewhat. I'll tell you what you want to choose, but for people

08:39 who have mental models, maybe using UVicorn, they could check out Hypercon, right? Yeah. So you

08:44 wouldn't need G-Unicorn because Hypercon has the process model that G-Unicorn adds to UVicorn.

08:49 And yeah, you can just switch over. It should be a one-liner because most of the time it's just that

08:54 one command line where you set this up, right? So yeah. Yeah. Very exciting. I'm going to give it,

08:58 give it a solid look because I'm deep in that world right now, playing with things, which is

09:04 very exciting, but not the topic for today. So the topic for today is people who are interested in

09:09 doing Flask and Quart, especially Quart, what are ways you can do more, right? So the frameworks

09:16 are pretty simple, right? That's of all the different APIs out there, the Flask API has

09:22 definitely won. You know, even though there's many different web frameworks, right? If you look at

09:28 the way programming works, you look at FastAPI, you look at a litestar, many of these things,

09:34 it's like you've got to create a thing called app and then you say app.get and so on. Right? So

09:39 this style of programming is really popular. I think it's because it's just straightforward and

09:44 simple opposite of Django, maybe not so many batteries included, but you can add the batteries

09:49 through extensions. Yeah. Yeah, exactly. I think that's always been the trade-off between Django

09:53 and Flask and perhaps even Django and Quart if you had to go Quart is Django makes the choice of

09:58 your batteries to a large extent, whereas Quart and Flask means you have to choose basically.

10:02 Yeah. Which is really nice if you got like, you know, like I would really rather use this graph

10:07 database for this thing. And I would really rather, you know, like you get a lot of choice,

10:11 but you have to, you're going to have to build a little bit more yourself and you've got to decide

10:14 more yourself. So, but with all these extensions, it starts to become easier to click the Lego

10:20 blocks back together. Yeah. Yeah, exactly. Yeah. All right. So if you go people listening, go to

10:26 quart.palettesproject.com and go in there and find the extensions. There's a great long list

10:33 of extensions that we've got here that they can choose to run, right. And how to do that and so

10:38 on. But you picked out a couple that you really want to highlight and we'll dive into them. And

10:42 if there's extra time, maybe we can, you know, pick some more out of the list and just see what

10:47 we can make of them. I think the best one to start with is a bit of discussion around an extension

10:52 called Quart Flask Patch, which is a bit of a odd name and maybe need to think of a better name for

10:57 it. But the idea behind this extension is it patches Quart at import time to look very much

11:04 like Flask. So you can use some Flask extensions directly. So this is again, the idea to make it

11:10 easier if you are so willing to transfer from Flask to Quart, this would allow you to an easier

11:16 step because you could add this one line in Quart Flask Patch and some of your Flask extensions

11:22 would just work. Now you've changed to Quart. That's awesome because there's a bunch out there.

11:28 So what happens when I run this? Is it basically say like, okay, there's a bunch of module namespaces

11:33 called Quart this, Quart that. Let's create proxies called Flask that just delegate into the Quart

11:39 elements and expose that back. So code written against Flask dot whatever, I think it still

11:44 exists. Yeah, exactly that. Yeah. It does slightly more because what it also needs to do is,

11:48 is because a lot of the Quart APIs are async. It needs to wrap them in a wrapper that allows it to

11:54 be called synchronously, but it needs to do so in a nested sense. And I don't know whether you've

11:59 looked much into like nesting event loops in one another or getting back to the same event loop.

12:05 It's a bit of a, I think it's fair maybe to call it a bit of a hack, but it does work well enough

12:12 to give you, to allow you to buy that time to develop on, but that's what it has to do. Yeah.

12:17 Yeah. I really wish that Python had an ambient event loop that was the default. This like,

12:24 give me an event loop. Oh, it doesn't exist in exception. Oh, creating an event loop. Sorry,

12:28 exception. It already exists. Like, you know, there's like, it's just really tricky to figure

12:32 out kind of deep down in some library. Like, well, am I in charge of this? Is it already going? And

12:37 I got to be part of something bigger. If you could just say, just async run this, you know, and just

12:41 you do that. You need to make an event loop, make it. If you don't need to make one, just put it in

12:46 the one that's already there. Right? Like I really wish there was a little bit cleaner story there,

12:50 but you know, we're not going to solve that with Quart extensions.

12:53 No, no. I mean, if they hadn't done that, it would have made making Flask async a lot easier.

12:59 You've probably come across the library. I think it's greenback.

13:02 Greenback. No, this is new.

13:03 So greenback is a green, like stack switcher based library that allows you to get back to

13:12 the event loop when you're deep in some sync code, which is quite nice in the sense that, you know,

13:18 it allows you to do all the stuff that's difficult, but it's not so nice in that it's kind of hidden

13:23 in a way monkey patching type stuff going on in the background.

13:26 Yeah. Yeah. Yeah.

13:27 I think this is what SQL alchemy uses to get their async await supporting. So it's certainly,

13:33 yeah, I guess popular. Yeah.

13:35 Sure. Reenter async IO or trio event loops from synchronous code. Very nice. Cool. Okay. So

13:41 Flask patch, that's pretty good if you just have some Flask code and you're like, oh,

13:46 I want to switch to Quart, but hopefully nothing, nothing changes. And I'll just run this at the

13:51 beginning and everything will work. How often does that, how often does it work out to be

13:55 easy like that?

13:56 So it depends on what the extension does. So if the extension does its own IO or calls certain

14:02 Flask functions that we can't, this extension can't patch, then yeah, it just fails. And

14:08 yeah, so not all of them. If you go into tests, you can see an example of some of them

14:13 that we test against or I test against, sorry, just to make sure they work.

14:17 Like login potentially.

14:18 Yeah. So login is probably the most popular, I think. So I don't think it works for SQL alchemy,

14:23 for example, there's a technical example, anywhere that uses app context as a, in a

14:29 contact manager in a synchronous location, basically that's too hard to patch. So they

14:34 tend to all fail, but yeah, a few do. And I hope it makes it easier for people. I'm pretty sure it

14:39 is used because I get bug reports. So some people find it useful at least.

14:42 Yeah, absolutely. It looks, it looks excellent. Right. So once you, once you do all the magic,

14:48 you can just work with the login manager and off it goes. Right.

14:51 Yeah, exactly. Yeah.

14:52 Super cool. This portion of talk Python to me is brought to you by Posit, the makers of Shiny,

14:58 formerly RStudio and especially Shiny for Python. Let me ask you a question. Are you building

15:04 awesome things? Of course you are. You're a developer or data scientist. That's what we do.

15:09 And you should check out Posit Connect. Posit Connect is a way for you to publish, share,

15:14 and deploy all the data products that you're building using Python. People ask me the same

15:20 question all the time. Michael, I have some cool data science project or notebook that I built.

15:25 How do I share it with my users, stakeholders, teammates? I need to learn FastAPI or flask,

15:30 or maybe Vue or react JS. Hold on now. Those are cool technologies, and I'm sure you'd benefit from

15:36 them, but maybe stay focused on the data project. Let Posit Connect handle that side of things.

15:41 With Posit Connect, you can rapidly and securely deploy the things you build in Python, Streamlet,

15:46 Dash, Shiny, Bokeh, FastAPI, Flask, Quarto, Ports, Dashboards, and APIs. Posit Connect supports all

15:54 of them. And Posit Connect comes with all the bells and whistles to satisfy IT and other enterprise

15:59 requirements. Make deployment the easiest step in your workflow with Posit Connect. For a limited

16:05 time, you can try Posit Connect for free for three months by going to talkpython.fm/posit.

16:11 That's talkpython.fm/posit. The link is in your podcast player show notes.

16:17 Thank you to the team at Posit for supporting Talk Python.

16:19 CORS. Everybody loves their cross-site origin scripting restrictions. I guess we do love it

16:29 in the sense that we're not getting our bank account stolen when we browse the web. But as

16:34 a web developer, you're like, "Oh, CORS again. I need to allow this thing to talk to that." But

16:39 it can be a hassle. And so I'm guessing CORS allows you to control those settings and expose

16:47 them and so on?

16:47 Yeah, exactly that. I think it's become more prevalent since the switch to front-end

16:52 frameworks has been so popular. Because you end up serving them, typically you end up serving

16:56 them separately. But yeah, it's exactly as you described. You can put decorators on routes,

17:02 or you can put it on a blueprint or on the entire app, and you just say, "This is a CORS policy for

17:06 everything." Yeah. Very similar to Flask CORS, but different API.

17:10 So you can put it on even a blueprint and say, "This whole section of code is where the APIs

17:15 live." Things can call that, but nothing else. That's really nice.

17:18 Yeah.

17:19 Okay.

17:19 So it also has a slight web... Because obviously, CORT does web sockets whereas Flask,

17:24 at least natively, can't. But yeah, there's some web socket considerations for CORT as well,

17:29 which mostly come around to checking the origin header. But yeah, it does that as well.

17:33 Okay. So you have a route and you just put the @route_cors on it, or like you said, a blueprint,

17:39 or on the whole app, or however you want. And then what kind of arguments does it take? What can I

17:44 ask it? Make it do?

17:45 Yeah. So I went for the functional API for it. So you can put in the allow headers or... What's

17:52 the other one? The credentials. I forget what it's called now. But they're all listed and all

17:56 keyword arguments effectively to it. There was in the issues, someone, maybe last year, put out

18:01 a suggestion for a new API for CORS libraries that would be more intuitive, but wouldn't match

18:07 the actual headers that came out. So at some point, I hope to look into that and offer that as well.

18:11 But at the moment, you basically say the header and then the values you want. So it's very...

18:15 You have to really know CORS to use it.

18:17 Okay. It just basically juggles the headers and there's not a lot of abstractions in between.

18:22 No, no. So I think in the readme, it shows you them all actually. So these are all the headers you can control. And it's basically the same name.

18:29 Sure. So access control, allow origin, access control, allow credentials, expose headers,

18:35 max age, all these things that you might want to use. Right. Very cool.

18:40 Yeah.

18:40 Okay. Oh, you have types as well. So that's lovely. Are you a fan of types? Type hints?

18:45 Since it came out, I've tried not to write untyped code. I think, especially in a team,

18:50 I think it makes a huge difference to the understanding of the code. So yeah, I'm a big fan.

18:54 Yeah. I also think it makes your editors help you more. If it knows what type that is and you say

18:59 dot, it'll... Is this what you meant? Like, yes, actually, I didn't know that existed,

19:03 but that's what I want. Give that to me. Without types, you're kind of like, well,

19:06 you could object or weird, useless stuff that you can't do anything with.

19:11 All right. So CORS, if you need to do CORS, there's, I guess, let me know how to do this,

19:16 but I think pretty much most of these that exist, there's probably a Flask variant. So if you use

19:21 Flask and you're like, oh, I need CORS, but I don't use CORT, just Flask-CORS instead of

19:26 CORT-CORS. Yeah. Yeah. I think it's quite well maintained, the Flask one. So yeah,

19:30 the Flask CORS, it's slightly different API, but the concepts are all the same, obviously. So yeah.

19:34 Another one is, auth means so many different things. Is it a session type-based auth? Is it

19:42 OAuth? Is it, who knows what, YubiKeys? What are we talking here?

19:46 Yeah. Session management via a secure cookie. So it's a similar usage if you're familiar with

19:52 Flask login, but I didn't want to call it CORT login because it didn't really do anything with

19:56 the login. It didn't check the password or the username or an MFA or anything to do with login,

20:00 in my opinion. So I called it auth instead. But yeah. So with this extension, you tell it to log

20:06 in the user and give it some kind of identifier and it saves that information securely in a cookie

20:12 for you. And whenever the user comes back and presents the cookie, it then pulls it out and

20:16 turns it into what is called the current user. So I think it's called the current user in Flask

20:21 login as well. So very similar to that. All right. So you get this auth user back.

20:24 What is this auth user? It's not like out of your database, right? It's some, it has properties.

20:29 By default, it's just as a method to say whether the user is authenticated and to return their ID.

20:36 But if you customize it, if you extend it, so what I typically do in my code base is I extend it and

20:41 I add methods to return a model from the database, for example. Yeah. Very cool. Nice. And you know,

20:48 there's a lot of fancy login auth stuff, but nice session cookie. You know, it just,

20:54 this works these days. It still works really well. Does yeah. Much simpler than JWTs and

20:59 less likely to go wrong, I think. It's not exactly related, but I've had a lot of websites now I've

21:06 given up on passwords, but they don't use something new or they just decided like every time you want

21:11 to get to the site, you just, we're going to email you a code and you enter it. When did that become

21:16 the way we're going to log into everything? It's driving me crazy. So some good old username,

21:21 password, we're going to store your ID in a cookie. It warms, warms the heart.

21:24 But I've been playing with the web auth in little USB keys and such recently. I quite like it. I

21:31 think it might be a bit complex still. It's a little tricky to get working, but yeah, I think

21:37 that's probably the future to be honest with you. The site just sees like, yeah, you got the right

21:40 hardware thing. Off we go. Peter knows you're there. Something like that. Right. Nice. Okay.

21:44 So people need to deal with users. This is not about storing them in the database, encrypting,

21:51 hashing their passwords or those types of algorithms. It's really just about the web

21:56 browser exchange that involves setting a cookie when you're logged in and getting it back and

22:00 make sure it's not tampered with. Right. Yeah. Yeah. So what it does is it stores the,

22:04 the value, like the, here, the number two is the ID for the user. So it stores that in the cookie

22:11 in plain text, but then cryptographically signs it. So you don't want to store anything

22:15 sensitive in the cookie if you're already about to be in red, but you can store,

22:20 identifies, et cetera, and know that it hasn't been tampered with.

22:22 Yeah. That's the thing. It's not going to harm you to know that your user ID is two or

22:27 seven, five, one, one, seven or whatever. But if it's two was to stop somebody from going,

22:33 let me change that to a three and see what happens. Oh, look, I'm an admin. How cool is that?

22:39 Right. That's the problem that you're trying to solve with secure, right?

22:42 Exactly. Yeah.

22:43 From my side, I bake that thing in myself, but having an extension that just does it,

22:47 that sounds better.

22:48 Yeah. So the nice thing, well, the bit I took a bit of interest, I think it's changed now

22:52 with Flask login, but a couple of the areas where I wanted to improve is it does same site

22:58 settings on the cookie by default. So it will start with the strict, and then you can play

23:02 around and go down to lax if you so need. But it also has the special, it allows the

23:07 special host, the double underscore host prefixes on the name and stuff like that.

23:12 Oh, right. Yeah.

23:13 Although I don't think in reality that adds that much to your security, but it's a safety net

23:18 in some respects.

23:19 Yeah. Sometimes, well, also it becomes a hassle when you're doing development because your host

23:24 is localhost and you're saying it has to be on mysite.com. You're like, well, I can't log in

23:28 anymore. So then you've got to have localhost. And then I don't know, it doesn't feel super

23:32 tamper resistant to me either. Are you familiar with securepy? There we go. This is an interesting

23:39 project too.

23:39 Oh, it's been archived.

23:40 Oh, it's archived. Oh, sadly. Is there a new one? No, but I think it still works. I'm not

23:45 sure what, been deprecated. Okay. Well, I guess that's not being used as well, used that much,

23:51 but it used to be a way to kind of set all those different things, I believe. Or is this different?

23:55 This is a totally different.

23:56 No, I think this is, I thought you were going to say secure headers. I think, is it called that?

24:00 Oh, I think it's just called secure. What it is. Yeah. That's the one. So sorry. That was some other

24:06 random thing that we've done that was called securepy, but not, this thing is still alive

24:10 and supports court, which is why I brought it up amongst like lots of other frameworks. But yeah,

24:15 you were going to tell people about this. This is cool.

24:17 Yeah. So I pretty much ended up adding all of these by hand. So yeah, you, most of the headers

24:24 you want to add, and if you ever do a pen test, right, they're going to, if you don't have these

24:28 headers by default, there's going to be a finding straight away. So yeah, it just adds to all the

24:32 ones you want really.

24:33 Right. For one of the ones that you might not think of that you should probably have,

24:37 unless you have, unless you have a really specific use case is what if somebody created a site,

24:42 you know, you had service.com and they had service.io or, you know, whatever, and they,

24:48 they bought that and then they could put your, whatever their URL is, your, it just into your

24:54 site as a hundred percent width and height iframe. Right. And then they just capture all the key

24:59 strokes. Like, huh, you're typing into your thing. It looks like your thing, your data is there.

25:04 You don't want people to do that to your site or just randomly host your site and their site. So

25:08 for example, this secure thing will set X frame options to same origin. So you can't embed.

25:14 Your site can no longer be embedded by default. You've got to choose to let it be embedded. All

25:17 those kinds of things. Right. Are really nice here.

25:20 I actually got caught out by that many, many years ago. I built a, an image photo editor

25:26 in the browser. It started to get quite a lot of use. I was like, Oh, this is great. And then I

25:30 realized it was on somebody else's domain, basically in an iframe.

25:33 So like, look at our amazing editor that we're getting Google AdWord money for or whatever.

25:38 Right. Yeah, exactly.

25:40 Yeah. So a little bit of a header action will shut that stuff down. But I mean, I didn't know to do

25:45 it until I kind of dug into it more, right. Or you find out your site somewhere else.

25:49 I think it's like a lot of it seems to be learned with experience, I guess, isn't it?

25:55 Or on the job.

25:55 It only happens once when you see your web app in somebody else's site embedded as an iframe. You're like, no, this is not going to be okay.

26:02 Awesome. All right. So that was court auth as well as the secure thing.

26:07 Next, I guess, kind of related to security, maybe not exclusively, but someone rate limiting.

26:14 Yeah, because I mean, on a login route, you'd want to put a rate limiter on

26:17 straightaway to stop any kind of brute forcing anyway, right?

26:19 So, oh, absolutely. And the ability to do really nice rate limiting in ways that punish the rate limit breaker is so much easier on async things like court,

26:31 right? Because if you don't want to be overwhelmed by them, a lot of times you just say you send them

26:36 up some kind of response code item with the status code is like too many requests or something like

26:40 some 400.

26:41 429.

26:42 There you go. So you can just send them back straightaway, but there's no cost for them

26:47 to receive that 429. You know, they're just like super quick going. If you want to say,

26:52 like, because maybe they're trying to guess passwords or something, you're like, let's not

26:55 let them guess so quickly. So you can like sleep and then respond to them in 10 seconds later.

27:02 So it's like, oh, well, I don't know if the password good or not. Why don't you wait around

27:06 for a while instead of going somewhere else with a async IO based website? That's a weight async

27:10 IO.sleep, nearly zero cost to you other maybe an open socket. And you can just like send them to

27:16 sort of time black holes, super easy. Whereas like a WSGI app, you're blocking a request and

27:21 you can only handle so many and all that kind of stuff. Right?

27:24 Yeah. So this one sadly doesn't do any of that fun.

27:27 It doesn't have punishment built into it. No, but tell us what, how it works and what it does.

27:32 I was going to say, I think there was an article today suggesting you should serve

27:35 zip bombs or something to like unpleasant requesters, but yeah, it doesn't do any of that.

27:40 I was going to send them like a hundred gigs back or something.

27:43 Yeah, zip bombs would be good. That would be good actually.

27:46 Yeah. So this one implements rate limiting as it's defined, like a user would be allowed to

27:51 do so many requests in a certain time and also implements the RFC for the like headers that

27:58 you're supposed to send back when they over, when they go over the rate limits. So they know when

28:02 they can, or when the client can then request again.

28:04 I see. Like you've made too many, rather than just saying too many requests,

28:08 try again later. You're like, and you'll be reset in five minutes or like there's a proper way to communicate that.

28:13 The thing I find really interesting about this, which I don't know if everyone does,

28:17 but I'll say anyway, but most people implement rate limits in like a leaky bucket or something

28:22 like that. And these are the standard algorithms. And typically that requires you to store two

28:27 variables per key per rate limit you're looking at. But there's a, I think it's not a very well

28:32 known algorithm called the generic cell rate algorithm. I think that's right. And this allows

28:38 you to store one algorithm at one variable per rate limit. And it's really quite nice.

28:43 The algorithm is very, when you, when you get it, it's really intuitive and clean. You basically,

28:48 what you store is when you expect the next request to arrive. And if the request arrives

28:52 too early, then you just rate limit it. And yeah, it's really clever. I think so. I'd like more

28:57 people to know about it basically, because it's a really nice solution.

29:00 It's a great solution. Yeah. So this, it doesn't slow them down. It just tells them that they've

29:05 gone over the limit in this particular one. Right. It's not like if you expect it to be in five

29:10 seconds, you don't just sleep for four and then let it in.

29:13 No, no. So the idea is that this decorator is much less costly in terms of your compute

29:18 than your function, your handler. So this decorator will just send back a four to nine

29:23 very quickly and never touch your route instead.

29:25 Okay. Interesting. I'm starting to think about maybe if you have async and await

29:30 available and your algorithm told you when the next one was coming, you could just

29:34 sleep them until the next one's allowed potentially if it wasn't 10 minutes.

29:38 Right. Potentially. The client, I mean, it depends on your rate limits, but there's a

29:42 good chance the client would timeout. Right. So.

29:44 Right. It'd have to be like five seconds or something real short. Right. Like I don't

29:48 want more than one request a second type of rate limiting, not like five per hour,

29:53 then they would definitely timeout. But yeah, there's a lot of a lot of things you can do

29:55 with this. So basically the way it works for people listening is you can set it up on the

30:01 app. So the entire app itself, all the requests to it are limited. Right. Or you can go and

30:07 put kind of like tenacity. You put the decorator exactly on a particular endpoint and then

30:13 set the details for that one. Right.

30:14 Yep. So a bit like cause you can do it per app, per blueprint or per route, depending

30:19 on what you need.

30:20 Okay. And here you say rate limit one and then a time Delta. What's the one?

30:23 It's the count. So you might want. So for a rate of one per second, you might want to

30:28 express that as 10 per 10 seconds. So it would be instead of the one, the 10. And that allows

30:33 you a bit of bursting if you will. So, you know, that 10 can come in one second or they

30:37 can be spread over. So yeah, it allows you to express the rate limits in different ways.

30:42 Really. Yeah. I've seen this probably bypasses the static files. That true?

30:47 If you do the default limits, I think that applies to static files as well. I can't remember

30:52 now, but I think that's what it does.

30:53 You might have, you know, six CSS files, 20 images and so on. Right. And like one page

30:58 request might blast those all back super quick.

31:01 Yeah. Yeah. You certainly want a higher limit.

31:03 Yeah. But presumably you're using something like Nginx or whatever, or maybe even a CDN.

31:08 And then all those other things I described don't actually go through the Python app.

31:13 Ideally.

31:14 Yeah. Yeah. I mean, it does work if you do it for the Python app as well. So up to you,

31:18 I think. Yeah.

31:18 Indeed. All right. WTForms. Next up, QuartWTF. What is this?

31:24 So this was, I'm tempted to say it's been a bit less popular recently with the front

31:30 end frameworks and stuff. But when you did template rendering, you'd render the form,

31:34 then you do the validation in your Python framework for that form. And you pass back

31:38 the errors. And basically it would, the WTF framework would do a lot of that for you.

31:44 So you could just put in like in your template, an input box, and it would do the errors and do

31:49 the, like you can see here, you could do the CSRF tokens and render all the HTML for the

31:56 labels and the actual input boxes and get it all hooked up very easily. And that's what it does.

32:00 Yeah. It does all that. And the validation, when it comes to the backend as well, I think

32:04 you can just do formed up, validate on submit or something like that. Yeah, there we are.

32:08 And that's it. That's your whole validation. So it saves a lot of effort, really.

32:12 Yeah. That's pretty handy. And I'm guessing you're getting generally generic HTML out on the client side so you can CSS style it up while you like.

32:21 Yeah. I think it's, I can't remember exactly. I think WTF might have hooks so you can

32:26 put styling directly in, but yeah, it's pretty, yeah, pretty generic as I remember it.

32:31 Yeah. Some frameworks require explicit stuff on say the input box, like form dash control would

32:37 be bootstrap and things like that. Right. There's probably some way to set a class.

32:41 Yeah. I think there's a class name argument. I'm trying to, I may have misremembered that.

32:46 I think it was called class name.

32:47 Yeah. Sure. Nice. Yeah. I haven't used this really, but I've used it kind of like it in

32:52 framework, other frameworks. In the end, I ended up just going back to writing my own

32:56 HTML because I felt like I was getting 60% help, but maybe 30% struggle to fight the system. You

33:04 know what I mean? And then like, all right, you know what? I just know this stuff well enough.

33:07 I'm just going to do it myself. And, and, but if you don't want to mess with it, you want to some

33:11 sort of quick development type of thing, or maybe you're not super familiar with HTML, this will

33:15 definitely help. Right.

33:16 Absolutely. Yeah. I think you can also write your own HTML instead and just let it do the

33:20 Python validation part.

33:21 All right. As long as it lines up. Yeah, sure.

33:23 Yeah. I think it's just the name that matters. Like your input has to have the right name.

33:27 Yeah. Otherwise how would it know? Right. It couldn't, it couldn't know.

33:30 Yeah, exactly. The CSRF is quite easy and nice. That just puts a hidden input in with the token

33:35 in and it's all taken care of for you. So that can be quite nice.

33:38 Yeah. Excellent. All right. Schema.

33:41 So schema.

33:42 For APIs, right?

33:43 Yeah. So it's possibly the kind of more, I'll say modern, but I'm not sure if that's the word I

33:47 mean, but yeah. So as an industry, we seem to have moved away from the forms of old and form

33:53 data to send in JSON all the time. Right. And a court schema is about saying, I expect this

33:59 structure to arrive, validate it for me. So it's a very similar, it's very similar to FastAPI,

34:04 which I'm sure a lot of people know.

34:06 Okay. That's pretty interesting. So like you say, very much like FastAPI, it kind of brings in

34:12 some frameworks called model binding, where you can say there's an argument and it's of this type.

34:17 And then, you know, like a class, a data class or something along those lines, and it'll, here,

34:22 it's a data class and it'll take all the inputs, right? Query strings or route primers, whatever,

34:28 and parse them and map them in there. And then just give you an object and go here, it's ready.

34:32 Yeah. So for this library, it supports, I've used all the documentation as data class, but it's

34:37 Pydantic based at the moment. And because of the, it's not actually the typing. So there's

34:41 no dependency injection or anything like that with FastAPI, but it uses the decorator validate

34:45 request and you pass it the model. And if the request that arrives can be turned into that

34:51 structure and validated as such, then your function will be called and that'd be passed in as data.

34:57 If it can't, then the client will get a 400 response.

34:59 Bad requests, bad data.

35:00 Exactly. Yeah.

35:01 Okay. What's the, so validate requests makes sense to me. It means like, here's your

35:06 structured object that comes in, passed in as data. What's validate response?

35:11 So it's the same concept, but going back. So if you return like a to do class instance itself,

35:18 then there's no validation to do, right? You've already said it's the right structure,

35:21 but often you'd return a dictionary or something like that, right? And expect it to go back as Jason. What this will do is it will say, I'll check that dictionary

35:30 is of the right structure. And if it isn't, it throws a 500 response to the client and an error

35:36 that you can go and view in whatever error monitoring tool you've got.

35:39 Sure. Okay. That's excellent. So I see you have data classes here and you said it also

35:43 works with the pydantic.

35:44 Yeah. So, well, actually a shout out to one of your previous recent streams is I'm working on

35:48 message spec support at the moment. Cause I actually quite like that. So.

35:52 Message spec is interesting.

35:53 So it will support pydantic and message spec. So you'd be able to use pydantic data classes

35:58 at as message spec stuff. Well, the message spec struct, I think that's probably all of it,

36:03 but yeah, quite a bit of choice.

36:04 The struct classes really, they've done some super interesting things here to make these

36:10 data classes almost better for performance than even regular, and for memory and stuff

36:14 than regular Python classes. It's quite impressive.

36:16 Yes, absolutely. Yeah.

36:18 Yeah. For people who don't know, maybe just tell them real quick what message spec is.

36:22 They probably know data classes and pydantic.

36:24 Yeah. So much like data classes, and you can see here, it's a nice, simpler way of creating a class with various things created for you.

36:32 But it also, much like pydantic will do validation when you try and if you use one of its functions

36:38 to convert raw data, like a dictionary into this type and much like pydantic as well,

36:43 it also gives the ability to take this model and turn it into a open API, JSON schema definition.

36:50 And that's the final thing that Quartz schema does is, once you've decorated all your routes,

36:55 it will auto generate a open API definition for you as well.

36:59 Oh, it will. Okay. Yeah. Using flaggers/docs. I think that's really nice. A lot of APIs just

37:05 don't, they're like, "We're not going to do that." If they don't have something automated to do it

37:09 and having typed classes representing all your inbound and outbound data, pretty easy, right?

37:14 Yeah. I mean, it really helps with mypy, right? Because instead of having like your request.data

37:19 or something like that, which is some dict that as far as mypy concerns can be anything,

37:24 it must be a to-do instance in this function now. So yeah, really helps with your type checking.

37:29 Yeah, absolutely. All right. What do we got next? Sock from Miguel Grimberg, Flask websockets.

37:36 This one, there is an extension for, because this is one of the areas that Quartz being

37:39 natively async can just do itself. So the Quartzer, I think I described sometimes as a

37:44 superset of Flask. So you can do everything Flask can do. And a bit more.

37:47 The C++ of Flask.

37:49 Yeah, exactly.

37:51 Awesome. So websockets, a problem that websites generally have not been able to do. Once you

37:58 make a request, usually there's the response and that's it. But what if the server, you want to

38:02 send more stuff to the server, everyone wants to send stuff out to you later. Like I started this

38:06 job three seconds later, now it's done. Or this bidirectional exchange is really cool. But a lot

38:12 of times I feel like it's a little over the top. Like a lot of times I think people maybe just want

38:17 push notifications from the server and not true. Like let's have a conversation. Like I just need

38:23 to know when this happens. Just let me know as soon as it's done. Right. So for that, yeah, go ahead.

38:27 Server sent events, sorry, seem to be not that well known, but yeah, probably described your

38:32 usage really well.

38:33 That's exactly what I was getting at. So does Quartz support SSE? Does it support

38:37 server sent events?

38:38 Yeah. Yeah. I think there's an extension for it, but in reality it's a very small bit of code you

38:44 need. So it, yeah, I mean, cause you literally return a response with a certain header and then

38:51 you just return a, well, here we go. Here's the, there's an example in the documentation for

38:56 Quartz, but you just return a certain data structure and then you've got it. Yeah.

39:00 Yeah. So I think that's when people say they want web sockets, I think most of the time this is what

39:05 they want. They want the client, the server to call the client sometimes, not just the other way

39:09 around.

39:09 Yeah. So I'm going to go slightly off topic cause it's interesting, but it's related to this, but

39:15 one of the things I implemented in the Hypercorn cause I find it really interesting is I think

39:19 two, no, it must be more like four years ago, they introduced web sockets for HTTP/2. And of

39:24 course, one of the reasons you want web sockets, because the cost of opening the connection for

39:28 HTTP/1 is fairly expensive. So you can keep it open and do the bi-directional, but with HTTP/2

39:34 you don't even need an extra connection anymore. You can have your web socket stream on the same

39:38 connection as all your requests as well. So it's really nice and efficient. So yeah, I'd love to

39:42 have an opportunity to make more use of it cause I find it really interesting, but the code I write

39:47 professionally at the moment doesn't quite need that level of real time interaction.

39:50 I know, I know all this stuff I built, I'm like, that would be so fun, but you know,

39:53 I just don't need that. I have no use case. I mean, there are apps I'm sure that do it right.

40:00 Like really complicated single page apps like StreamYard that we're using right now, for

40:05 example, this has probably got some crazy interchange going on, but I don't write this

40:09 kind of apps either. All right. Let's see. So the socket stuff, which is cool. That's,

40:16 like you said, built into Court. That's one of the advantages of it, but you know,

40:19 you can use Miguel Grimberg's stuff to add it to Flask. SQLAlchemy, you mentioned them before,

40:25 probably outside, if you're not in the Django space, it's not the only option for sure,

40:30 but it's probably the de facto choice people, when they think I want an ORM for a relational

40:35 database, you probably start here, right? Yeah, I think you would. Yeah. And this is a port,

40:41 as I understand it, of the Flask SQLAlchemy extension to work with, or natively work with

40:47 Court. So should match all your expectations, I think. And if you're used to it, you just

40:53 use this and it works with Court, and there you go. Let's see an example here. You can set up

40:58 your database with a pretty interesting nested type of construct here, like just, you know,

41:05 passing rich arguments that take more. And if you look at the code on there, just the shape of the

41:10 code is kind of unique here, but you end up with this constructed database thing, and then it has

41:16 the base model class and off you go, right? You just create a session from it, do all the things.

41:22 I've always had mixed feelings about these things that you set up here that are built, like,

41:26 into the web frameworks, essentially, that make them part of the request,

41:29 because if you're going to go and write little scripts that also do data stuff, you want to

41:34 be able to get access to the data and the models over there as well. Although this looks like it

41:38 does take the app there, but I don't know if it really needs a meaningful app. Like, I don't

41:43 think that it would use it, probably, but it just adds it to the request. No, I don't think you need

41:47 to be within an app context to use the DB. I may be wrong there, but yeah, I think you could probably

41:52 use it in a script. Yeah. >> Yeah, that's the only thing that concerns me about these, like,

41:56 database helpers is, like, you know, I need to do database stuff outside of a request as well.

42:01 Even potentially some of the stuff that we're going to talk about in a minute, like, it's just

42:04 a background task that's not part of an actual request, but it could even still be in the web

42:09 server process, right? >> I use, there's an extension called Cort DB, which is my preference,

42:13 because I prefer to write the SQL and go down the ORM route. And yeah, that works as you'd expect.

42:21 You'd still set it up and pass it the app, which I think is true of this, but then you can just use

42:26 it without an app context. It just basically uses the app to get the configuration values,

42:31 so it knows where the database is. >> Interesting. And now we're on to probably the biggest one that

42:36 you want to talk about, which is quite exciting, Fort Tasks. >> Yeah, so this is a very recent

42:42 release that I haven't announced anywhere else yet. This is something, I think I read a post

42:48 recently that said, for your web framework, you need, like, five things. Like, you need

42:53 a database connection, you need some kind of validation, input/output, rate limiting,

42:57 authentication, and you need Array to run periodic tasks. And in the past, my way to do this was

43:04 extra infrastructure. So it was Cron running somewhere, or some kind of, I think CloudWatch

43:10 does some kind of event generator based on a Cron schedule. And for me, that's just annoying,

43:16 because now I have to build extra infrastructure, which I don't need. I'm not at that scale.

43:20 >> Yeah. Maybe you've got Celery or RabbitMQ or something like that, right?

43:23 >> Yeah, exactly. But again, with those, you need that extra infrastructure for it to work.

43:27 >> Yeah. It's another server. It's another thing that could get hacked. You've got to patch it.

43:31 You've got to secure it. You've got to know how to run it. It could go down, and then

43:34 none of your background stuff works anymore. It's all a hassle, even though it has a benefit.

43:39 >> Yeah. So what I really want to do is make use of the async event loop and just run a task in

43:45 the background periodically. And obviously, at some point, this will normally only scale,

43:49 because I have so many background tasks that dominate the server and stop it doing requests.

43:53 But while I'm small and I'm starting out, this just makes life so much easier.

43:56 >> Absolutely. It totally does. And because everything's async and await,

44:00 you can just set up the task and just await the task. And it just blends in,

44:05 hopefully, well with all the web requests, right?

44:07 >> Yeah. Yeah. So for core tasks, if you write your task as async def, then it will run it in the event loop. If you write it as a def, it will run it on a thread.

44:17 So if you have synchronous code that's going to block the event loop, like you're using

44:21 traditional I/O for want of a better phrase, then if you just use def, it will run it on a task,

44:26 won't block your event loop, and you'll be fine. So yes, it's nice in that respect.

44:30 >> Yeah. And as long as your async pieces are fine-grained enough, you probably won't know.

44:37 >> Yeah.

44:37 >> Yeah. It's just if you run a whole bunch of synchronous code or something like that,

44:41 you might start to clog things up, but don't do that. Honestly, what I find anyway on the web

44:48 most of the time is you're talking to other things, talking to a database, talking to a cache,

44:52 talking to an API. All those things are perfectly suited for await, because most of the time,

44:59 you're almost outside the web app.

45:00 >> Yeah. I think the perfect one for this that I keep thinking of is when you want to send a

45:05 periodic email, like a reminder or a summary of the day, just write this decorator task run,

45:11 and you have your function that just sends emails. Yeah, that's why it exists for me.

45:15 It makes that so much easier.

45:16 >> Yeah, absolutely. Right. Yeah. Like you say, just send out a quick email, like, "Hey,

45:20 I need to reset my password." All right, here, we're going to send you call an API,

45:23 like SendGrid or something like that. We're going to send you, but you don't want to block

45:27 things up. You just kick that onto the background work and off it goes.

45:31 One of the things that people would say, "All right, you guys, I know you're excited about

45:36 async, but we need durability. What about, what if the server goes down? Or what if another

45:43 potential issue is what if you scale, like at least the cron stuff? Like what if you

45:48 ban out your web app into like 10 worker processes?" And you say, "Run in five seconds."

45:53 And they all run in five seconds. TEN times or those kinds of issues here.

45:59 >> So this extension supports a couple of extensions to it. So you can pass it a custom

46:06 store, which allows you to store when the task last ran. And so that means if the server

46:11 goes down and it misses an invocation, then when it comes back up, it will recognize that

46:15 and run the task straight away rather than wait till the next time. And the other thing

46:20 it supports is you can decide what should happen before the task starts and what should

46:25 happen after. So this allows you to put some locking in for your second case. In both cases,

46:31 the reason it's not built in to core tasks is you'll need to decide where you store this

46:34 information, where do you store the lock? Where do you store the... So usually a database

46:38 works for that. So yeah, so you can add it all, but you've got to choose how yourself.

46:42 >> That's cool that it has placeholders and ways to plug that in. That's what I was thinking

46:47 I was right. Like instead of just storing it in memory and throwing it on the queue,

46:50 put it into the database and have the task just check the database and go, okay, I'm

46:55 working on this one. No one else do this. We don't want to send 10 emails to the person.

46:59 I got it. Right. All that kind of stuff. >> It's not a job queue, although I've been

47:03 thinking about that recently. So yeah, it can't be like, it doesn't work in the sense

47:07 that job will run once, it will do jobs periodically. >> Got it. Okay. So more of a scheduler

47:13 type of thing. So I need to see if anyone's uploaded files to this location and then convert

47:19 it from PNG to WebP or like safe space or... >> You could be like, every morning I want

47:24 to send out an email to the users and say, imagine it's a to-do app because everyone

47:28 uses that as an example and say, these are your tasks for the day or something like that.

47:31 >> Sure. People who've signed up for the daily digest, send them their digest, right?

47:37 >> Yeah. >> Yeah. Very cool. And because it's on a background thread, it's not going to harm anything. Excellent. Okay. Well, it

47:43 looks pretty simple again. Very decorator driven, right? >> Yeah. So I mean, with all

47:48 of these, the extensions that they start for it and I've tried to follow the kind of flask

47:52 conventions for better, for worse. So yeah. Same with Quart, it follows Flask. >> Yeah.

47:56 It feels like if you're already doing Flask or Quart, you just know what to do. It's just

48:00 similar. >> Yeah. >> Excellent. I think we've covered all the ones we explicitly said.

48:06 Let's make sure we get a chance to cover those. I definitely want to encourage people to check

48:09 out the Quart extensions page because there's quite a few more. I'll see if maybe some jump

48:15 out at me here that are kind of cool. Quart B. >> We briefly mentioned Quart DB, which is one

48:20 I maintain. So I could say a bit more about that if you're so interested. >> Yeah, sure. And it's

48:24 hanging out right here on your list. >> So this is much like the SQLAlchemy one. It's a way to

48:30 connect to a database, but unlike the SQLAlchemy way, it doesn't promote an ORM. Rather, it just

48:36 gives you a connection that you can run SQL on. I don't know you can do that with SQLAlchemy. I

48:41 just, I mean, typically that's not what people do. So in this case, like the example here is you

48:46 pass it the URL you want to connect to and it supports Postgres and SQLite at the moment.

48:52 And then in the roots, you have this G.connection object that you can make use of. And you can make

48:57 use of that in background tasks. And it's quite easy to make use of it actually in Quart tasks

49:02 as well. It's just a little snippet you need to do. But yeah, and that's it. You get a connection

49:06 that you can do whatever you want with. >> Yeah, okay. Very nice way to just have everything set

49:11 up right. Like you don't have to worry about passing things around and create a transaction,

49:15 go do your things, right? Excellent. We got Quart events for broadcasting server sent events and

49:21 WebSockets. That's pretty cool. >> There is one, yeah. >> Yeah. Another one that's cool is

49:25 minifying all of your elements, right? CSS JavaScript. >> I've been doing that in the build

49:30 stage a lot recently. There's a little snippet I can share if people want for Quart where you

49:35 change the static function. So it will serve a gzipped version of the static file over the

49:42 un-gzipped one if the client will accept it. And yeah, then if you do it build time,

49:46 because I think this one, and it might be a mistake, but I suspect it works at runtime.

49:50 So it compresses at runtime. >> It sounds like it does, yeah. It talks about doing it as a response,

49:55 right? So yeah. Oh, it does have a cache though. >> Ah, okay. Yeah. Where you get similar, yeah,

50:00 very similar to what I'm suggesting there. >> Excellent. Now those are really, really handy.

50:03 If people go and they want to make their website better, check out, I guess, PageSpeed.

50:09 >> Or the Lighthouse. >> Lighthouse is what I was going to call it. Yeah, but they renamed it

50:13 to PageSpeed. So you can go put in, and talk Python and see how we're doing. But it'll give

50:18 you things like how is your site fast enough? Let's see how the desktop one's doing, right?

50:23 It'll give you a score and are we going to index you well, or are you going to be punished for

50:29 having some kind of crappy experience? And this minification stuff that we're talking about can

50:33 help there, right? >> Absolutely, yeah. >> Similar to your DB, your QuarkDB, we have QuarkMotor,

50:39 which is the async database access library for MongoDB. So I suspect it's super similar. I

50:45 haven't used it because I use Beanie, which is like ORM style. So kind of on top of those things.

50:50 Keycloak is interesting, or Redis also is pretty interesting. Uploads, let's finish it out with

50:56 uploads. We'll call it after that. So I haven't used this, but uploading files can always be a

51:01 bit of a hassle, right? >> It looks very similar to the Flask uploads one. So it's kind of a nice

51:06 wrapper around how you get access to the files you've uploaded and what you do with them kind

51:11 of thing, yeah. >> Yeah, that can be a little bit of a hassle, right? You got your request.files

51:16 and all that. Don't know how to use this thing, but I know that people kind of struggle uploading

51:21 files a lot, so that can be a thing. >> I don't tend to actually write it that much come to think

51:26 of it, but yeah, it just makes it easier. >> Yeah, exactly. Profile pictures, maybe,

51:32 is like that kind of stuff I'd be doing. Anyway, I guess I've wrote some code that lets you upload

51:37 MP3s at one point for podcasts, but I stopped using it. So it's not top of mind anymore. Anyway,

51:44 excellent stuff here. I love just going through and seeing all these little pieces, these little

51:48 building blocks you can bring together and just quickly build an app. So thanks for being on the

51:52 show and telling everyone about it. >> Oh, thank you. Thank you, yeah. Great, hopefully, opportunity

51:57 to introduce some stuff that I hope people will find useful. >> Yeah, and just more broadly,

52:01 like thanks for working on Quart and pushing that forward. >> Yeah, I mean, like I said, our aim is

52:06 hopefully we'll, well, we're kind of going around in different ways of how we actually might merge

52:11 Flask and Quart, but that's still the ultimate aim. I kind of think because of the limitations

52:16 we were talking about earlier about running async code when you're deep in async code base,

52:21 I think you'll always have to make a choice with Flask where if you're going to be mostly sync,

52:25 you go Flask. If you're mostly async, you go an async Flask or whatever it's called,

52:29 which at the moment it's called Quart. We might be able to put them in the same code base perhaps,

52:33 so then you can maybe mix and match because you can certainly run Flask and Quart together with

52:39 Hypercorn as the server and just have a, what's called a dispatch middleware layer in between

52:44 that says for these routes, it goes to Flask and for those routes, it goes to Quart. So yeah,

52:48 there are lots of ways to do it, but it depends what you want to do really. >> Yeah, that's

52:52 interesting. Maybe you could even create a different app, sort of app equals Flask,

52:56 it's app equals async Flask, who knows. >> That's effectively what Quart is. I mean,

53:01 I could have called it async Flask at the start. >> Yeah, you're right. A Flask, yeah, ranks higher

53:07 in the alphabetical sorting. All right, so people are excited with Quart. Maybe it's back on their

53:12 radar now that we've been talking about all these extensions. What do you tell them? How do they get

53:16 started? What do they do? >> I mean, you can just pip install Quart and a quick start is as simple

53:22 and familiar as it's ever been for Flask. You just do app equals Quart and then app.get or app.whatever

53:28 and then your function below to handle it. >> Yeah, the selling point is basically,

53:32 if you know Flask, you know Quart. You just use the word async some of the time. >> Yeah, yeah,

53:36 yeah. I mean, even the WebSocket support, I've tried to make it as familiar as it would be if

53:41 you had new Flask. So instead of using the global request, you now use the global WebSocket. So

53:46 there's WebSocket.headers and you can do WebSocket.send, send a message, that kind of stuff.

53:50 So hopefully it just feels familiar. >> Yeah, fantastic. I'm sure it does. And

53:55 thanks for being on the show and nice to have you back. >> Thank you. >> See you next time.

53:58 >> Yeah, you bet. This has been another episode of Talk Python to Me. Thank you to our sponsors.

54:04 Be sure to check out what they're offering. It really helps support the show.

54:07 This episode is sponsored by Posit Connect from the makers of Shiny. Publish, share,

54:12 and deploy all of your data projects that you're creating using Python. Streamlet, Dash, Shiny,

54:18 Bokeh, FastAPI, Flask, Quarto, Reports, Dashboards, and APIs. Posit Connect supports all of them.

54:25 Try Posit Connect for free by going to talkpython.fm/posit, P-O-S-I-T. Want to level up your

54:32 Python? We have one of the largest catalogs of Python video courses over at Talk Python.

54:36 Our content ranges from true beginners to deeply advanced topics like memory and async. And best

54:42 of all, there's not a subscription in sight. Check it out for yourself at training.talkpython.fm.

54:47 Be sure to subscribe to the show, open your favorite podcast app, and search for Python.

54:52 We should be right at the top. You can also find the iTunes feed at /iTunes, the Google Play feed

54:57 at /play, and the Direct RSS feed at /rss on talkpython.fm. We're live streaming most of our

55:04 recordings these days. If you want to be part of the show and have your comments featured on the

55:08 air, be sure to subscribe to our YouTube channel at talkpython.fm/youtube. This is your host,

55:15 Michael Kennedy. Thanks so much for listening. I really appreciate it. Now get out there and

55:19 write some Python code. [Music]

55:40 [ required co presence in the comment section below ]

Back to show page
Talk Python's Mastodon Michael Kennedy's Mastodon