WEBVTT

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

00:00:05.080 --> 00:00:10.960
who's working closely with the Flask team on these parallel projects. The TLDR version is

00:00:10.960 --> 00:00:15.360
that if you want to take advantage of async and await and you're using Flask, you want to give

00:00:15.360 --> 00:00:20.580
Quart a solid look. We've spoken to Philip previously about Quart. This time around,

00:00:20.580 --> 00:00:25.280
he's here to share his top Quart extensions and libraries that you can adopt today.

00:00:25.820 --> 00:00:31.040
This is Talk Python To Me, episode 452, recorded January 10th, 2024.

00:00:31.040 --> 00:00:50.560
Welcome to Talk Python To Me, a weekly podcast on Python. This is your host, Michael Kennedy.

00:00:50.560 --> 00:00:55.660
Follow me on Mastodon, where I'm @mkennedy and follow the podcast using @talkpython.

00:00:55.660 --> 00:01:01.620
both on fosstodon.org. Keep up with the show and listen to over seven years of past episodes

00:01:01.620 --> 00:01:07.940
at talkpython.fm. We've started streaming most of our episodes live on YouTube. Subscribe to our

00:01:07.940 --> 00:01:13.920
YouTube channel over at talkpython.fm/youtube to get notified about upcoming shows and be part of

00:01:13.920 --> 00:01:19.860
that episode. This episode is sponsored by Posit Connect from the makers of Shiny. Publish, share,

00:01:19.860 --> 00:01:25.140
and deploy all of your data projects that you're creating using Python. Streamlit, Dash, Shiny,

00:01:25.500 --> 00:01:32.520
OKFastAPI, Flask, Quattro, Reports, Dashboards, and APIs. Posit Connect supports all of them. Try

00:01:32.520 --> 00:01:39.940
Posit Connect for free by going to talkpython.fm/Posit, P-O-S-I-T. And it's also brought to you

00:01:39.940 --> 00:01:47.660
by us over at Talk Python Training. Did you know that we have over 250 hours of Python courses? Yeah,

00:01:47.660 --> 00:01:52.840
that's right. Check them out at talkpython.fm/courses. Philip, welcome back to Talk Python.

00:01:52.840 --> 00:01:55.820
Tommy. Great to have you here. Thank you. Thank you for the invite. We're going to talk about web

00:01:55.820 --> 00:02:01.960
things. I love some web things. We're going to talk about async. I love async. And we're going to talk

00:02:01.960 --> 00:02:06.500
about a lot of little cool things you can plug into your web apps that make them more awesome.

00:02:06.500 --> 00:02:13.620
And we give a lot of people a lot of things to think about. So yeah, let's do it. So I guess

00:02:13.620 --> 00:02:18.720
quickly before we get into talking about Quart, maybe a little catch up on where it's been since the

00:02:18.720 --> 00:02:23.620
last time we spoke about that. Give folks a bit of background on yourself who don't know you.

00:02:23.620 --> 00:02:29.980
Sure. Yeah. I guess probably in this context and maybe the people listening are probably known

00:02:29.980 --> 00:02:35.940
for Quart and Hypercorn and helping maintain the pallets libraries like Flask and Virksuk.

00:02:35.940 --> 00:02:41.960
But well, outside of that, I guess in my professional career, I work as a software engineer in London.

00:02:41.960 --> 00:02:47.620
I work for a company called Curaleaf International. And quite interestingly, because it's not very well

00:02:47.620 --> 00:02:52.160
known, we're one of the few companies that do medical cannabis in the UK. Not many people know

00:02:52.160 --> 00:02:58.040
it's legal. Yeah. Put that aside. So I've worked there for about a year and a bit now. Before that,

00:02:58.040 --> 00:03:02.040
worked at various other software firms. And then before that, I was actually a particle physicist

00:03:02.040 --> 00:03:05.600
working on an experiment out in Canada in a deep mine, which was quite a good fund.

00:03:05.600 --> 00:03:09.100
So you like your variety of your jobs, I can tell.

00:03:09.100 --> 00:03:10.780
Yeah. Had a definite career shift.

00:03:10.780 --> 00:03:17.040
Indeed. Just a funny sidebar, you know, cannabis was legalized here three or four years ago in Oregon.

00:03:17.040 --> 00:03:23.160
There are more cannabis shops than there are Starbucks, than there are McDonald's. Like

00:03:23.160 --> 00:03:30.360
I could walk to four from my house in five minutes. It's nuts. Anyway, it's very different than it was a

00:03:30.360 --> 00:03:35.560
few years ago, for sure. It's all fine, but just different. All right. So court, like Flask,

00:03:35.560 --> 00:03:41.460
kind of, but very interesting. It's got some other capabilities. Tell people about court. How's it

00:03:41.460 --> 00:03:46.860
relate to Flask? There's a lot of crossover. You and David Lord are working pretty closely,

00:03:46.860 --> 00:03:48.600
it seems like, on things these days.

00:03:48.600 --> 00:03:54.180
I started court about, I guess, seven, maybe a bit more years ago now. And the original idea was

00:03:54.180 --> 00:04:00.720
make Flask async. At the time, I couldn't figure out how to adapt Flask at all in that direction. So I

00:04:00.720 --> 00:04:05.980
thought, okay, I'll start by just re-implementing the Flask API using async await. And that's what

00:04:05.980 --> 00:04:11.660
court started out to be. Over the years, David and I have worked to try and merge the two together.

00:04:11.660 --> 00:04:17.700
So the very latest change that's happened a few months ago is we actually based court on Flask

00:04:17.700 --> 00:04:23.380
now. So there's a lot of shared code now. And for a long time, court was based on VirgSug. And if

00:04:23.380 --> 00:04:28.460
you're familiar with the palettes and Flask ecosystem, Flask is built on VirgSug. And court was,

00:04:28.460 --> 00:04:31.880
but now court is built on Flask, which is built on VirgSug. And they're very, very similar.

00:04:31.880 --> 00:04:34.580
Yeah. Oh, that's fantastic. Because the more that you can share,

00:04:34.580 --> 00:04:40.120
like the more you all can just focus on features and making it better rather than duplicating that

00:04:40.120 --> 00:04:40.400
effort.

00:04:40.400 --> 00:04:45.580
The other aim is that the APIs are the same as they can be other than async await. So you

00:04:45.580 --> 00:04:49.940
can take your Flask code base and move to court just by adding async await. That's the dream,

00:04:49.940 --> 00:04:51.780
even closer than it's ever been to that now.

00:04:51.780 --> 00:04:56.220
You're pretty close to be able to rename the word Flask, keeping the case sensitivity

00:04:56.220 --> 00:04:59.680
intact from Flask to court. And it works, right?

00:04:59.680 --> 00:05:03.980
Yeah. In simple projects, yeah. The more complicated ones, it comes down to the extensions,

00:05:03.980 --> 00:05:05.360
which we're going to talk about.

00:05:05.360 --> 00:05:08.460
Yeah. That's the whole topic of today, right?

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

00:05:13.140 --> 00:05:18.900
and be able to say async Flask. Well, it's kind of the case now where you can say async Flask is court

00:05:18.900 --> 00:05:24.520
and we can't quite do what Django has done and put async Django as it is in Django. Ours,

00:05:24.520 --> 00:05:29.500
you have to make a naming choice from the start. But yeah, I think it's getting as close as we can get.

00:05:29.500 --> 00:05:31.500
Maybe we'll figure out how to merge them in the future.

00:05:31.500 --> 00:05:37.720
Yeah. It'd be great if you could choose to use async def or just regular functions for your views

00:05:38.360 --> 00:05:39.760
and not really think about it.

00:05:39.760 --> 00:05:40.420
You can't do that.

00:05:40.420 --> 00:05:40.940
Okay.

00:05:40.940 --> 00:05:44.820
You've been able to do that for about two years, I think, trying to figure out when we merge that.

00:05:44.820 --> 00:05:48.680
So you could use either. It's just not as performant. If your code base is going to be

00:05:48.680 --> 00:05:52.820
mostly async, you probably want to switch to court fully because it'd be more performant

00:05:52.820 --> 00:05:54.580
and more reliable and robust.

00:05:54.580 --> 00:05:55.460
Sure. Why is that?

00:05:55.460 --> 00:05:59.880
The Flask implementation will run your async code on a separate thread.

00:05:59.980 --> 00:06:04.420
So you get all that overhead of switching between the threads, sending the information over and back,

00:06:04.420 --> 00:06:04.840
et cetera.

00:06:04.840 --> 00:06:09.480
Right. Does it run like an asyncio event loop on the other thread, but then

00:06:09.480 --> 00:06:12.960
basically curry the results back and forth?

00:06:12.960 --> 00:06:13.180
Yep.

00:06:13.180 --> 00:06:18.740
And with court, it's running right on top of an ASGI server straight, right?

00:06:18.740 --> 00:06:19.000
Yeah.

00:06:19.000 --> 00:06:24.700
Yeah. That's super cool. And you might be responsible for one of the more prominent ones out there.

00:06:24.700 --> 00:06:28.460
Tell people about, well, what is an ASGI server for people who don't know?

00:06:28.560 --> 00:06:30.600
Why does it exist? And then, you know, the one that you got.

00:06:30.600 --> 00:06:35.520
Yeah, sure. So historically there was the whiskey standard, which separated servers from

00:06:35.520 --> 00:06:40.420
frameworks applications and kind of gave you a choice between how do you want to serve

00:06:40.420 --> 00:06:44.400
and deal with concurrency and stuff? And then how do you want your API to build your apps,

00:06:44.400 --> 00:06:48.160
the framework to be? I think it was a great choice because it made the ecosystem really,

00:06:48.160 --> 00:06:54.900
really nice for developers. And ASCII is the same thing for async. And so court is a,

00:06:54.900 --> 00:06:58.360
is an example of an ASCII framework and Hypercon is an ASGI server.

00:06:58.360 --> 00:07:04.100
They originally together, much like Sanic started out as well, but then I split that out Hypercon

00:07:04.100 --> 00:07:08.980
into its own thing. And then later on now, Hypercon is also a wsgi server. So yeah,

00:07:08.980 --> 00:07:12.160
in some respects, Hypercon is just a general purpose server for Python.

00:07:12.160 --> 00:07:21.740
And so Hypercon, it lives in the same general usage base as Gunicorn and MicroWSGI UWSGI,

00:07:22.280 --> 00:07:27.120
right. So I could run it in production in a kind of like a overseer mode where you can fan out

00:07:27.120 --> 00:07:32.280
process, worker processes and system daemon, system D, all that, that, that where it's at.

00:07:32.280 --> 00:07:38.000
Yeah, it's exactly like that. Yeah. Yeah. And it also in the ASCII space, it's similar to Uvicorn or

00:07:38.000 --> 00:07:39.680
Daphne. That's it. Right.

00:07:39.680 --> 00:07:42.020
I think there's another, but I've forgotten the name just now.

00:07:42.020 --> 00:07:48.820
Yeah. I was just messing with some Docker stuff and I was running the web app, a FastAPI app,

00:07:48.820 --> 00:07:57.440
NGunicorn using uvicorn workers. So handling the ASGI stuff that way, is it better to use Hypercorn?

00:07:57.440 --> 00:08:01.980
Is it rather than something along those lines, a little more direct maybe, or what do you think?

00:08:02.060 --> 00:08:06.880
I think it's a bit of a choice. So how it differs from uv, well, Uvicorn in general is quicker because

00:08:06.880 --> 00:08:12.720
it's HTTP parser. HP1 parser is quicker. And there's a few other choices that tend to make it a bit

00:08:12.720 --> 00:08:19.120
quicker. However, this last time I checked, Uvicorn doesn't support as many extensions as Hypercorn and

00:08:19.120 --> 00:08:23.780
it doesn't support HTTP2 or HTTP3. Now, most people don't actually serve that directly. They tend to have

00:08:23.780 --> 00:08:28.080
a load balancer in front, but I tend to do that because it's quite exciting. So you're getting there

00:08:28.080 --> 00:08:32.820
with Hypercorn. And then after that, it's a bit of a choice between, you know, what features do they

00:08:32.820 --> 00:08:35.980
both have? What do you want to use? So they're fairly similar, I would say.

00:08:35.980 --> 00:08:40.580
Sure. Okay. Somewhat. Up to you, what do you want to choose? But for people who have mental models,

00:08:40.580 --> 00:08:43.780
maybe using uvicorn, they can check out Hypercorn, right?

00:08:43.780 --> 00:08:48.920
Yeah. Yeah. So you wouldn't need Gunicorn because Hypercorn has the process model that Gunicorn adds to

00:08:48.920 --> 00:08:54.060
uvicorn. And yeah, you can just switch over. It should be a one-liner because most of the time it's just

00:08:54.060 --> 00:08:56.840
that one command line where you set this up, right? So yeah.

00:08:57.120 --> 00:09:02.480
Yeah. Very exciting. I'm going to give it a solid look because I'm deep in that world right now,

00:09:02.480 --> 00:09:07.920
playing with things, which is very exciting, but not the topic for today. So the topic for today is

00:09:07.920 --> 00:09:15.100
people who are interested in doing Flask and Quart, especially Quart, what are ways you can do more,

00:09:15.100 --> 00:09:20.900
right? So the frameworks are pretty simple, right? That's of all the different APIs out there,

00:09:20.900 --> 00:09:26.660
the Flask API has definitely won. You know, even though there's many different web frameworks,

00:09:26.660 --> 00:09:33.300
right? If you look at the way programming works, you look at FastAPI, you look at a Litestar,

00:09:33.300 --> 00:09:38.520
many of these things, it's like you've got to create a thing called app and then you say app.get and so

00:09:38.520 --> 00:09:43.660
on, right? So this style of programming is really popular. And I think it's because it's just

00:09:43.660 --> 00:09:48.980
straightforward and simple. Opposite of Django, maybe. Not so many batteries included, but you can

00:09:48.980 --> 00:09:50.500
add the batteries through extensions. Yeah.

00:09:50.620 --> 00:09:54.560
Yeah, exactly. I think that's always been the trade-off between Django and Flask and perhaps

00:09:54.560 --> 00:09:59.380
even Django and Quart if you were to go Quart is Django makes the choice of your batteries to a large

00:09:59.380 --> 00:10:02.620
extent, whereas Quart and Flask means you have to choose basically. Yeah.

00:10:02.680 --> 00:10:07.300
Yeah. Which is really nice if you got like, you know, like I would really rather use this graph

00:10:07.300 --> 00:10:11.720
database for this thing. And I would really rather, you know, like you get a lot of choice, but you

00:10:11.720 --> 00:10:15.040
have to, you're going to have to build a little bit more yourself and you've got to decide more

00:10:15.040 --> 00:10:20.420
yourself. So, but with all these extensions, it starts to become easier to click the Lego blocks

00:10:20.420 --> 00:10:21.120
back together. Yeah.

00:10:21.240 --> 00:10:22.040
Yeah, exactly. Yeah.

00:10:22.040 --> 00:10:30.560
All right. So if you go, people listening, go to court.palettesproject.com and go in there and find

00:10:30.560 --> 00:10:35.920
the extensions. There's a great long list of extensions that we've got here that they can

00:10:35.920 --> 00:10:40.840
choose to run, right? And how to do that and so on. But you picked out a couple that you really want

00:10:40.840 --> 00:10:45.900
to highlight and we'll dive into them. And if there's extra time, maybe we can, you know, pick some

00:10:45.900 --> 00:10:47.860
more out of the list and just see what we can make of them.

00:10:48.000 --> 00:10:52.380
I think the best one to start with is the bit of discussion around an extension called

00:10:52.380 --> 00:10:57.480
court flask patch, which is a bit of a odd name and maybe need to think of a better name

00:10:57.480 --> 00:11:04.520
for it. But the idea behind this extension is it patches court at import time to look very

00:11:04.520 --> 00:11:10.500
much like flask. So you can use some flask extensions directly. So this is again, the idea

00:11:10.500 --> 00:11:15.780
to make it easier. If you are so willing to transfer from flask to court, this would allow you to

00:11:15.780 --> 00:11:21.540
an easier step because you could add this one line and court flask patch and some of your flask

00:11:21.540 --> 00:11:24.860
extensions would just work. Now you've changed the court.

00:11:24.860 --> 00:11:30.440
That's awesome because there's a bunch out there. So what happens when I run this? Does it basically

00:11:30.440 --> 00:11:36.240
say like, okay, there's a bunch of module namespaces called court this court that let's create proxies

00:11:36.240 --> 00:11:41.960
called flask that just delegate into the court elements and expose that back. So code written

00:11:41.960 --> 00:11:47.720
against flask dot whatever think it still exists. Yeah, exactly that. Yeah, it does slightly more because it

00:11:47.720 --> 00:11:53.340
what it also needs to do is because a lot of the court APIs are async. It needs to wrap them in a wrapper

00:11:53.340 --> 00:11:59.220
that allows it to be called synchronously, but it needs to do so in a nested sense. And I don't know whether

00:11:59.220 --> 00:12:05.240
you've looked much into like nesting event loops in one another that you're getting back to the same event loop.

00:12:05.440 --> 00:12:17.720
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 to give you to allow you to buy that time to develop on. But that's what it has to do. Yeah.

00:12:17.720 --> 00:12:23.760
Yeah. I really wish that Python had an ambient event loop. That was the default. This like,

00:12:23.760 --> 00:12:29.460
get me an event loop. Oh, it doesn't exist in exception. Oh, create an event loop. Sorry, exception. It already

00:12:29.460 --> 00:12:34.220
exists. Like, you know, there's like, it's just really tricky to figure out kind of deep down in

00:12:34.220 --> 00:12:38.200
some library, like, well, am I in charge of this? Is it already going? And I got to be part of something

00:12:38.200 --> 00:12:43.420
bigger. If you could just say, just async run this, you know, and just you do that. You need to make an

00:12:43.420 --> 00:12:47.020
event loop, make it. If you don't need to make one, just put it in the one that's already there,

00:12:47.020 --> 00:12:52.460
right? Like I really wish there was a little bit cleaner story there, but you know, we're not going to

00:12:52.460 --> 00:12:56.960
solve that with court extensions. No, no. I mean, if they had have done that, it would have made making

00:12:56.960 --> 00:13:03.720
Flask async a lot easier. You've probably come across the library. I think it's Greenback. Greenback. No, this is new.

00:13:03.720 --> 00:13:14.760
So Greenback is a green like stack switcher based library that allows you to get back to the event loop when you're deep in some

00:13:14.760 --> 00:13:19.660
sync code, which is quite nice in the sense that, you know, it allows you to do all the stuff that's

00:13:19.660 --> 00:13:25.700
difficult, but it's not so nice in that it's kind of hidden in a way, monkey patching type stuff going on

00:13:25.700 --> 00:13:31.240
in the background. Yeah. Yeah. Yeah. I think this is what SQLAlchemy uses to get their async await

00:13:31.240 --> 00:13:38.320
supporting. So it's certainly, yeah, I guess popular. Yeah. Sure. Reenter asyncio or trio event loops from

00:13:38.320 --> 00:13:45.140
synchronous code. Very nice. Cool. Okay. So Flask patch, that's pretty good if you just have some

00:13:45.140 --> 00:13:50.500
Flask code and you're like, oh, why don't you switch to court? But hopefully nothing, nothing changes and

00:13:50.500 --> 00:13:54.820
I'll just run this at the beginning and everything will work. How often does that, how often does it

00:13:54.820 --> 00:14:01.400
work out to be easy like that? So it depends on what the extension does. So if the extension does its own

00:14:01.400 --> 00:14:08.160
IO or calls certain Flask functions that we can't, this extension can't patch, then yeah, it just fails.

00:14:08.160 --> 00:14:14.720
And yeah, so not all of them. If you go into tests, you can see an example of some of them that we test

00:14:14.720 --> 00:14:19.420
against or I test against, sorry, just to make sure they work. Like login potentially? Yeah. So login is

00:14:19.420 --> 00:14:25.480
probably the most popular, I think. So I don't think it works for SQLAlchemy, for example. There's a

00:14:25.480 --> 00:14:32.180
technical example, anywhere that uses app context as a, in a contact manager in a synchronous location,

00:14:32.180 --> 00:14:37.540
basically that's too hard to patch. So they tend to all fail, but yeah, a few do. And I hope it makes

00:14:37.540 --> 00:14:41.560
it easier for people. I'm pretty sure it is used because I get bug reports. So some people find it

00:14:41.560 --> 00:14:48.060
useful at least. Yeah, absolutely. It looks, looks excellent, right? So once you, once you do all the,

00:14:48.060 --> 00:14:52.900
the magic, you can just work with the login manager and off it goes, right? Yeah, exactly that. Yeah.

00:14:52.900 --> 00:14:58.060
Super cool. This portion of Talk Python To Me is brought to you by Posit, the makers of Shiny,

00:14:58.060 --> 00:15:05.260
formerly RStudio, and especially Shiny for Python. Let me ask you a question. Are you building awesome

00:15:05.260 --> 00:15:10.060
things? Of course you are. You're a developer or data scientist. That's what we do. And you should

00:15:10.060 --> 00:15:16.060
check out Posit Connect. Posit Connect is a way for you to publish, share, and deploy all the data

00:15:16.060 --> 00:15:21.960
products that you're building using Python. People ask me the same question all the time. Michael,

00:15:21.960 --> 00:15:26.560
I have some cool data science project or notebook that I built. How do I share it with my users,

00:15:26.560 --> 00:15:34.000
stakeholders, teammates? Do I need to learn FastAPI or Flask or maybe Vue or React.js? Hold on now.

00:15:34.000 --> 00:15:38.260
Those are cool technologies, and I'm sure you'd benefit from them, but maybe stay focused on the

00:15:38.260 --> 00:15:43.480
data project. Let Posit Connect handle that side of things. With Posit Connect, you can rapidly and

00:15:43.480 --> 00:15:49.680
securely deploy the things you build in Python. Streamlit, Dash, Shiny, Bokeh, FastAPI, Flask,

00:15:49.680 --> 00:15:56.820
Quarto, ports, dashboards, and APIs. Posit Connect supports all of them. And Posit Connect comes with

00:15:56.820 --> 00:16:02.560
all the bells and whistles to satisfy IT and other enterprise requirements. Make deployment the easiest

00:16:02.560 --> 00:16:07.580
step in your workflow with Posit Connect. For a limited time, you can try Posit Connect for free

00:16:07.580 --> 00:16:15.520
for three months by going to talkpython.fm/Posit. That's talkpython.fm/P-O-S-I-T. The link is in

00:16:15.520 --> 00:16:19.860
your podcast player show notes. Thank you to the team at Posit for supporting Talk Python.

00:16:21.920 --> 00:16:29.780
Cores. Everybody loves their cross-site origin scripting restrictions. I guess we do love it in

00:16:29.780 --> 00:16:35.520
the sense that we're not getting our bank account stolen when we browse the web. But as a web developer,

00:16:35.520 --> 00:16:41.520
you're like, oh, Cores again. I need to allow this thing to talk to that, but it can be a hassle.

00:16:41.520 --> 00:16:47.800
And so I'm guessing Cores allows you to control those settings and expose them and so on?

00:16:47.860 --> 00:16:52.560
Yeah, exactly. I think it's become more prevalent, right? Since the switch to kind of front-end

00:16:52.560 --> 00:16:56.800
frameworks has been so popular, right? Because you end up serving them, well, typically you end up

00:16:56.800 --> 00:17:02.220
serving them separately. But yeah, it's exactly as you described. You can put decorators on roots,

00:17:02.220 --> 00:17:06.380
or you can put it on a blueprint or on the entire app, and you just say, this is a Cores policy for

00:17:06.380 --> 00:17:10.700
everything. Yeah. Very similar to Flask Cores, but different API.

00:17:10.700 --> 00:17:15.600
So you can put it on even like a blueprint and say, this whole section of code is where the APIs live.

00:17:15.880 --> 00:17:19.000
Things can call that, but nothing else. That'd be really, that's really nice.

00:17:19.000 --> 00:17:19.520
Yeah. Okay.

00:17:19.520 --> 00:17:24.800
So it also has a slight web, because obviously Quart does WebSockets where it's Flask,

00:17:24.800 --> 00:17:29.620
at least natively can't. But yeah, there's some WebSocket considerations for Quart as well,

00:17:29.620 --> 00:17:32.860
which mostly come around to checking the origin header. But yeah, it does that as well.

00:17:32.860 --> 00:17:38.560
Okay. So you have a route, and you just put the at route underscore Cores on it, or like you said,

00:17:38.560 --> 00:17:43.720
a blueprint or on the whole app or however you want. But, and then what kind of arguments does it take?

00:17:43.720 --> 00:17:45.760
What can I ask it, make it do?

00:17:45.760 --> 00:17:50.840
Yeah. So I went for the kind of, I guess, functional kind of API for it. So you can put in like the

00:17:50.840 --> 00:17:54.980
allow headers or what's the other one, the credentials, I forget what it's called now,

00:17:54.980 --> 00:17:59.820
but they're all listed and all keyword arguments effectively to it. There was in the issues,

00:17:59.820 --> 00:18:05.120
someone a couple of, maybe last year, put out a new, a suggestion for a new API for Cores libraries

00:18:05.120 --> 00:18:09.840
that would be more intuitive, but wouldn't match the actual headers that came out. So at some point,

00:18:09.840 --> 00:18:13.340
I hope to like look into that and offer that as well. But at the moment that you basically say

00:18:13.340 --> 00:18:17.720
the header and then the values you want. So it's very, you have to really kind of know calls to use it.

00:18:17.720 --> 00:18:22.880
Okay. It just basically juggles the headers and there's not a lot of abstractions in between.

00:18:22.880 --> 00:18:27.700
No, no. So I think on the, in the readme, it shows you them all actually. So these are all the headers

00:18:27.700 --> 00:18:29.340
you can control and it's basically the same name.

00:18:29.420 --> 00:18:36.480
Sure. So access control, allow origin, access control, allow credentials, expose headers, max age,

00:18:36.480 --> 00:18:40.240
all this, all these things that you might, you might want to use, right? Yeah. Very cool.

00:18:40.240 --> 00:18:45.900
Yeah. Okay. Oh, you have types as well. So that's lovely. Are you a fan of types, type hints?

00:18:45.900 --> 00:18:50.200
Since it came out, I've tried not to write untyped code. I think, especially in a team,

00:18:50.200 --> 00:18:54.940
I think it makes a huge difference to the understanding of the code. So yeah, I'm a very big fan.

00:18:54.940 --> 00:18:59.480
Yeah. I also think it makes your editors help you more. You know, if it knows what type that is

00:18:59.480 --> 00:19:03.400
and you say dot, it'll, is this what you meant? Like, yes, actually, I didn't know that existed,

00:19:03.400 --> 00:19:07.300
but that's what I want. Give that to me. Without types, you're kind of like, well, you know,

00:19:07.300 --> 00:19:12.400
you could object or weird, useless stuff that you can't do anything with. All right. So cores,

00:19:12.400 --> 00:19:17.180
if you need to do cores, there's, I guess, let me know how to do this, but I think pretty much

00:19:17.180 --> 00:19:22.600
most of these that exist, there's probably a flask variant. So if you use flask and you're like,

00:19:22.600 --> 00:19:27.360
oh, I need cores, but I don't use court, just flask-cores instead of court-cores.

00:19:27.360 --> 00:19:31.040
Yeah. Yeah. I think it's quite well maintained, the flask one. So yeah, the flask cores,

00:19:31.040 --> 00:19:35.340
it's slightly different API, but the concepts are all the same, obviously. So yeah, not very much.

00:19:35.340 --> 00:19:43.920
Another one is auth means so many different things. Is it a session type based auth? Is it OAuth? Is it,

00:19:43.920 --> 00:19:46.640
who knows what, YubiKeys? What are we talking here?

00:19:46.640 --> 00:19:52.300
Yeah. Session management via a secure cookie. So it's a similar usage, if you're familiar with

00:19:52.300 --> 00:19:56.480
flask login, but I didn't want to call it court login because it didn't really do anything with

00:19:56.480 --> 00:20:00.800
the login. It didn't check the password or the username or an MFA or anything to do with login,

00:20:00.800 --> 00:20:06.840
in my opinion. So I called it auth instead. But yeah, so with this extension, you tell it to log in the

00:20:06.840 --> 00:20:12.660
user and give it some kind of identifier and it saves that information securely in a cookie for you.

00:20:12.660 --> 00:20:17.800
And whenever the user comes back and presents the cookie, it then pulls it out and turns it into

00:20:17.800 --> 00:20:22.740
what is called the current user. So I think it's called the current user in flask login as well. So

00:20:22.740 --> 00:20:27.020
very similar to that. All right. So you get this auth user back. What is this auth user? It's not like

00:20:27.020 --> 00:20:33.800
out of your database, right? It has properties. By default, it's just as a method to say whether the user is

00:20:33.800 --> 00:20:40.320
authenticated and to return their ID. But if you customize it, if you extend it. So what I typically do in my

00:20:40.320 --> 00:20:46.300
codebases, I extend it and I add methods to return a model from the database, for example.

00:20:46.300 --> 00:20:53.300
Yeah, very cool. Nice. And, you know, there's a lot of fancy login off stuff, but nice session cookie.

00:20:53.300 --> 00:20:57.040
You know, it just, this works these days. It still works really well.

00:20:57.040 --> 00:21:02.080
It does. Yeah. Much simpler than JWTs and less likely to go wrong. I think.

00:21:02.580 --> 00:21:08.720
It's not exactly related, but I've had a lot of websites now I've given up on passwords, but they

00:21:08.720 --> 00:21:13.100
don't use something new or they just decided like every time you want to get to the site, you just,

00:21:13.100 --> 00:21:17.820
we're going to email you a code and you enter it. When did that become the way we're going to log into

00:21:17.820 --> 00:21:22.720
everything? It's driving me crazy. So some good old username password. We're going to store your ID in a

00:21:22.720 --> 00:21:24.500
cookie. It warms, warms the heart.

00:21:24.500 --> 00:21:32.220
I've been playing with the WebOrph and little USB keys and such recently. I quite like it. I think

00:21:32.220 --> 00:21:37.700
it might be a bit complex still. It's a little tricky to get working, but yeah, I think that's

00:21:37.700 --> 00:21:40.640
probably the future to be honest with you. The site just sees like, yeah, you got the right

00:21:40.640 --> 00:21:45.020
hardware thing. Off we go. Peter knows you're there. Something like that, right? Yeah. Nice. Okay. So

00:21:45.020 --> 00:21:52.100
people need to deal with users. This is not about storing them in the database, encrypting, hashing

00:21:52.100 --> 00:21:57.780
their passwords or those types of algorithms. It's really just about the web browser exchange that

00:21:57.780 --> 00:22:01.460
involves setting a cookie when you're logged in and getting it back and make sure it's not tampered with,

00:22:01.460 --> 00:22:08.140
right? Yeah. Yeah. So what it does is it stores the value, like the here, the number two is the ID

00:22:08.140 --> 00:22:14.200
for the user. So it stores that in the cookie in plain text, but then cryptographically signs it. So you

00:22:14.200 --> 00:22:18.640
don't want to store anything sensitive in the cookie if you're worried about it being read,

00:22:18.640 --> 00:22:22.700
but you can store identifiers, et cetera, and know that it hasn't been tampered with.

00:22:22.700 --> 00:22:29.420
Yeah. That's the thing. It's not going to harm you to know that your user ID is two or 75117 or

00:22:29.420 --> 00:22:35.860
whatever. But if it's two was to stop somebody from going, let me change that to a three and see what

00:22:35.860 --> 00:22:41.760
happens. Oh, look, I'm an admin. How cool is that? Right. That's the problem that you're trying to

00:22:41.760 --> 00:22:43.340
solve with secure, right? Exactly. Yeah.

00:22:43.380 --> 00:22:47.940
Well, from my side, I baked that thing in myself, but having an extension that just does it,

00:22:47.940 --> 00:22:52.660
that sounds better. Yeah. So the nice thing, well, the bit I took a bit of interest, I think it's

00:22:52.660 --> 00:22:57.720
changed now with Flask Login, but a couple of the areas where I wanted to improve is it does same site

00:22:57.720 --> 00:23:02.780
settings on the cookie by default. So it will start with the strict and then you can play around and

00:23:02.780 --> 00:23:09.180
go down to lax if you so need. But it also has the special, it allows the special host, you know,

00:23:09.180 --> 00:23:14.500
the double underscore host prefixes on the name and stuff like that. So although I don't think in

00:23:14.500 --> 00:23:19.340
reality that adds that much to your security, but it's a kind of a safety net in some respects.

00:23:19.340 --> 00:23:24.840
Yeah. Sometimes also it becomes a hassle when you're doing development because your host is local host

00:23:24.840 --> 00:23:29.680
and you're saying it has to be on mysite.com. You're like, well, I can't log in anymore. So then you've

00:23:29.680 --> 00:23:34.240
got to have local hosts. And then I don't know, it doesn't feel super hamper resistant to me either.

00:23:34.240 --> 00:23:39.760
Are you familiar with SecurePy? This is an interesting project too.

00:23:39.760 --> 00:23:40.660
Oh, it's been archived.

00:23:40.660 --> 00:23:46.340
Oh, it's archived. Oh, sadly. Is there a new one? No, but I think it still works. I'm not sure what

00:23:46.340 --> 00:23:51.220
been deprecated. Okay. Well, I guess that's not being used as well, that are used that much,

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

00:23:55.300 --> 00:23:56.440
This is a totally different.

00:23:56.440 --> 00:24:00.860
Yeah. I think this is, I thought you were going to say secure headers, I think. Is it called that?

00:24:00.860 --> 00:24:05.960
Oh, I think it's just called secure what it is. Yeah. That's the one. So sorry. That was some

00:24:05.960 --> 00:24:10.840
other random thing that we've done that was called SecurePy, but not, this thing is still alive and

00:24:10.840 --> 00:24:16.020
supports court, which is why I brought it up amongst like lots of other frameworks. But yeah,

00:24:16.020 --> 00:24:17.420
you were going to tell people about this. This is cool.

00:24:17.420 --> 00:24:24.940
Yeah. Yeah. So I pretty much end up adding all of these by hand. So yeah, most of the headers you want

00:24:24.940 --> 00:24:28.560
to add. And if you ever do a pen test, right, they're going to, if you don't have these headers by

00:24:28.560 --> 00:24:31.980
default, there you go, that's going to be a finding straight away. So yeah, it just adds

00:24:31.980 --> 00:24:33.160
all the ones you want really.

00:24:33.160 --> 00:24:37.100
Right. For one of the ones that you might not think of that you should probably have,

00:24:37.100 --> 00:24:42.320
unless you have, unless you have a really specific use case is what if somebody created a site,

00:24:42.320 --> 00:24:48.360
you know, you had service.com and they had service.io or, you know, whatever. And they,

00:24:48.360 --> 00:24:54.680
they bought that and then they could put your, whatever their URL is, your, just into your site

00:24:54.680 --> 00:24:59.500
as a hundred percent width and height iframe. Right. And then they just capture all the keystrokes

00:24:59.500 --> 00:25:04.080
like, huh, you're typing into your thing. It looks like your thing. Your data is there.

00:25:04.080 --> 00:25:08.660
You don't want people to do that to your site or just randomly host your site and their site. So

00:25:08.660 --> 00:25:14.540
for example, this secure thing will set X frame options to same origin. So you can't embed your site

00:25:14.540 --> 00:25:18.340
can no longer be embedded by default. You've got to choose to let it be embedded. All those kinds of

00:25:18.340 --> 00:25:24.060
things, right. Are really nice here. Actually got caught out by that many, many years ago. I built

00:25:24.060 --> 00:25:29.400
a, an image photo editor in the browser. It started to get quite a lot of juice. I was like,

00:25:29.400 --> 00:25:33.860
oh, this is great. And then I realized it was on somebody else's domain, basically in an iframe.

00:25:33.860 --> 00:25:38.660
They're like, look at our amazing editor that we're getting Google AdWord money for or whatever.

00:25:38.660 --> 00:25:44.220
Right. Yeah, exactly. Yeah. So a little bit of a header action will shut that stuff down,

00:25:44.220 --> 00:25:48.940
but I mean, I didn't know to do it until I kind of dug into it more. Right. Or you find out your

00:25:48.940 --> 00:25:54.220
site somewhere else. Yeah. I think it's like a lot of it seems to be, yeah, learned with experience,

00:25:54.220 --> 00:25:58.860
I guess, isn't it? Or on the job. It only happens once when you see your web app in somebody else's

00:25:58.860 --> 00:26:04.740
site embedded as an iframe, right? No, this is not going to be okay. Awesome. All right. So that was

00:26:04.740 --> 00:26:12.140
court auth as well as the secure thing. Next, I guess, kind of related to security, maybe not exclusively,

00:26:12.140 --> 00:26:15.820
but somewhat rate limiting. Yeah. Cause I mean, on a login route,

00:26:15.820 --> 00:26:19.820
you'd want to put a rate limiter on straight away to stop any kind of brute forcing anyway. Right.

00:26:19.820 --> 00:26:27.180
So. Oh, absolutely. And the ability to do really nice rate limiting in ways that punish the rate

00:26:27.180 --> 00:26:33.420
limit breaker is so much easier on async things like court, right? Because if you don't want to be

00:26:33.420 --> 00:26:38.220
overwhelmed by them, a lot of times you just say you send them some kind of response code. I don't

00:26:38.220 --> 00:26:41.340
know what the status code is like too many requests or something like some 400.

00:26:41.340 --> 00:26:42.060
429.

00:26:42.060 --> 00:26:42.460
429.

00:26:42.460 --> 00:26:45.900
Yeah, there you go. So you can just send them back straight away, but there's no

00:26:45.900 --> 00:26:52.220
cost for them to receive that 429. You know, they're just like super quick going. If you wanted to say

00:26:52.220 --> 00:26:56.540
like, because maybe they're trying to guess passwords or something, you're like, let's not let them guess

00:26:56.540 --> 00:27:02.620
so quickly. So you can like sleep and then respond to them in 10 seconds later. So it's like,

00:27:02.620 --> 00:27:06.380
oh, well, I don't know. Is the password good or not? Why don't you wait around for a while

00:27:06.380 --> 00:27:11.980
instead of going somewhere else? With a asyncio based website, that's await asyncio.sleep,

00:27:11.980 --> 00:27:17.900
nearly zero cost to you, other maybe an open socket. And you can just like send them to sort of time

00:27:17.900 --> 00:27:22.380
black holes, super easy. Whereas like a WSGI app, you're blocking a request and you can only handle

00:27:22.380 --> 00:27:24.060
so many and all that kind of stuff. Right?

00:27:24.060 --> 00:27:27.420
Yeah. So this one sadly doesn't do any of that fun.

00:27:27.420 --> 00:27:32.220
It doesn't have punishment built into it. No, but tell us how it works, what it does.

00:27:33.100 --> 00:27:36.540
I was going to say, I think there was an article today suggesting you should serve zip bonds or

00:27:36.540 --> 00:27:40.780
something to like unpleasant requesters, but yeah, it doesn't do any of that.

00:27:40.780 --> 00:27:42.460
Send them like a hundred gigs back.

00:27:42.460 --> 00:27:45.980
Yeah. That would be good. That would be good actually.

00:27:45.980 --> 00:27:51.660
Yeah. So this one implements rate limiting as it's defined, like a user would be allowed to do so

00:27:51.660 --> 00:27:58.540
many requests in a certain time and also implements the RFC for the like headers that you're supposed to

00:27:58.540 --> 00:28:03.980
send back when they over, when they go over the rate limits. So they know when they can, or when the client

00:28:03.980 --> 00:28:08.380
can then request again. I see like you've made too many, rather than just saying too many requests,

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

00:28:13.980 --> 00:28:18.620
The thing I find really interesting about this, which I don't know if everyone does, but I'll say anyway, but

00:28:18.620 --> 00:28:24.300
most people implement rate limiting like a leaky bucket or something like that. These are the standard algorithms.

00:28:24.780 --> 00:28:30.460
And typically that requires you to store two variables per key per rate limit you're looking at.

00:28:30.460 --> 00:28:35.980
But there's a, I think it's not a very well known algorithm called the generic cell rate algorithm. I

00:28:35.980 --> 00:28:42.300
think that's right. And this allows you to store one algorithm at one variable per rate limit. And it's

00:28:42.300 --> 00:28:47.420
really quite nice. The algorithm is very, when you, when you get it, it's really intuitive and clean. You

00:28:47.420 --> 00:28:53.500
basically, what you store is when you expect the next request to arrive. And if the request arrives too early,

00:28:53.500 --> 00:28:57.900
then you just rate limit it. And yeah, it's really clever, I think. So I'd like more people to know

00:28:57.900 --> 00:29:00.060
about it basically, because it's a really nice solution.

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

00:29:05.740 --> 00:29:10.700
gone over the limit in this particular one, right? It's not like if you expect it to be in five seconds,

00:29:10.700 --> 00:29:13.020
you don't just sleep for four and then let it in.

00:29:13.020 --> 00:29:19.100
No, no. So the idea is that this decorator is much less costly in terms of your compute than

00:29:19.100 --> 00:29:24.860
your function, your handler. So this decorator will just send back a 429 very quickly and never

00:29:24.860 --> 00:29:29.420
touch your root instead. Okay. Interesting. I'm starting to think about maybe if you have

00:29:29.420 --> 00:29:34.700
a sink and a wait available and you, your algorithm told you when the next one was coming, you could just

00:29:34.700 --> 00:29:39.020
sleep them until the next one's allowed potentially, if it wasn't 10 minutes, right?

00:29:39.020 --> 00:29:42.940
That would be the client. I mean, it depends on your rate limits, but there's a good chance

00:29:42.940 --> 00:29:47.020
the client would time out, right? So, right. It'd have to be like five seconds or, or something

00:29:47.020 --> 00:29:51.980
real short, right? Like I don't want more than one request a second type of rate limit, not like

00:29:51.980 --> 00:29:55.740
five per hour that then they would definitely time out. But yeah, there's a lot of, a lot of things you

00:29:55.740 --> 00:30:02.140
can do with this. So basically the way it works for people listening is you can set it up on the app.

00:30:02.140 --> 00:30:08.140
So the entire app itself, all the requests to it are limited, right? Or you can go and put kind of like

00:30:08.140 --> 00:30:14.380
tenacity, you can put the decorator exactly on a particular endpoint and then set the details for

00:30:14.380 --> 00:30:18.700
that one, right? Yep. So a bit like cause, you can do it per app, per blueprint or per route,

00:30:18.700 --> 00:30:23.660
depending on what you need. Okay. And here you say rate limit one, and then a time delta. What's the one?

00:30:23.660 --> 00:30:29.580
It's the count. So you might want, so for a rate of one per second, you might want to express that as

00:30:29.580 --> 00:30:34.940
10 per 10 seconds. So it would be instead of the one, the 10, and that allows you a bit of bursting,

00:30:34.940 --> 00:30:39.500
if you will. So, you know, that 10 can come in one second or it can be spread over. So yeah,

00:30:39.500 --> 00:30:42.940
it allows you to express the rate limits in different ways, really.

00:30:42.940 --> 00:30:47.100
Yeah. I think this probably bypasses the static files. Is that true?

00:30:47.100 --> 00:30:52.780
If you do the default limits, I think that applies to static files as well. I can't remember now,

00:30:52.780 --> 00:30:53.500
but I think that's what it does.

00:30:53.500 --> 00:30:58.380
Yeah. Because you might have, you know, six CSS files, 20 images and so on, right? And like

00:30:58.380 --> 00:31:01.340
one page request might blast those all back super quick.

00:31:01.340 --> 00:31:03.180
Yeah. Yeah. You certainly want a higher limit.

00:31:03.180 --> 00:31:09.660
Yeah. But presumably you're using something like Nginx or whatever, or maybe even a CDN. And then

00:31:09.660 --> 00:31:14.060
all those other things I described don't actually go through the Python app, ideally.

00:31:14.060 --> 00:31:18.700
Yeah. Yeah. I mean, it does work if you do it for the Python app as well. So up to you, I think.

00:31:18.700 --> 00:31:18.940
Yeah.

00:31:18.940 --> 00:31:24.380
Indeed. All right. WT forms. Next up, court WTF. What is this?

00:31:24.380 --> 00:31:30.860
So this was, I'm tempted to say it's been a bit less popular recently with the front end frameworks

00:31:30.860 --> 00:31:36.140
and stuff. But when you did template rendering, you'd render the form, then you do the validation

00:31:36.140 --> 00:31:40.940
in your Python framework for that form and you pass back the errors. And basically it would,

00:31:40.940 --> 00:31:47.340
the WTF framework would do a lot of that for you. So you could just put in like in your template,

00:31:47.340 --> 00:31:53.180
an input box and it would do the errors and do the, like you can see here, you could do the CSRF tokens

00:31:53.740 --> 00:31:59.500
and render all the HTML for the labels and the actual input boxes and get it all hooked up very

00:31:59.500 --> 00:32:03.580
easily. And that's what it does. Yeah. It's a, it does all that. And the validation when it comes

00:32:03.580 --> 00:32:07.500
to the backend as well, I think you can just do form dot validate on submit or something like that.

00:32:07.500 --> 00:32:12.380
Yeah, there we are. And that's it. That's your whole validation. So it saves a lot of effort, really.

00:32:12.380 --> 00:32:18.860
Yeah, that's pretty handy. And I'm guessing you're getting generally generic HTML out on the client

00:32:18.860 --> 00:32:21.020
side so you can CSS style it up all you like.

00:32:21.020 --> 00:32:23.100
Yeah, I think it's, I can't

00:32:23.100 --> 00:32:28.380
remember exactly. I think WTF might have hooks so you can put styling directly in, but yeah,

00:32:28.380 --> 00:32:31.020
it's, it's pretty, yeah, pretty generic as I remember it.

00:32:31.020 --> 00:32:37.420
Yeah. Some frameworks require explicit stuff on say the input box, like form dash control would

00:32:37.420 --> 00:32:41.100
be bootstrap and things like that. Right. There's probably some way to set a class.

00:32:41.100 --> 00:32:44.860
Yeah. I think there's a class name argument. I'm trying to, I may have,

00:32:44.860 --> 00:32:47.260
may have misremembered that. I think it was called class name.

00:32:47.260 --> 00:32:52.140
Yeah, sure. Nice. Yeah. I haven't used this really, but I've used it kind of like it in

00:32:52.140 --> 00:32:56.380
framework, other frameworks. In the end, I ended up just going back to writing my own

00:32:56.380 --> 00:33:04.540
HTML because I felt like I was getting 60% help, but maybe 30% struggle to fight the system. You

00:33:04.540 --> 00:33:07.660
know what I mean? And then like, all right, you know what? I just know this stuff well enough.

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

00:33:11.580 --> 00:33:15.420
sort of quick development type of thing, or maybe you're not super familiar with HTML. This will

00:33:15.420 --> 00:33:19.980
definitely help. Right. Absolutely. Yeah. I think you can also write your own HTML instead and just let

00:33:19.980 --> 00:33:23.260
it do the Python validation part. All right. Long as it lines up. Yeah, sure.

00:33:23.260 --> 00:33:27.020
Yeah. Yeah. I think it's just the name that matters. Like your input has to have the right name.

00:33:27.020 --> 00:33:30.060
Yeah. Because otherwise how would it know, right? It couldn't, it couldn't know.

00:33:30.060 --> 00:33:35.340
Yeah, exactly. The CSRF is quite easy and nice. That just puts a hidden input in with the token in,

00:33:35.340 --> 00:33:38.140
and it's all taken care of for you. So that can be quite nice.

00:33:38.140 --> 00:33:40.700
Yeah. Excellent. All right. Schema.

00:33:40.700 --> 00:33:41.820
So schema.

00:33:41.820 --> 00:33:43.020
For APIs, right?

00:33:43.020 --> 00:33:48.060
Yeah. So it's possibly the kind of more, I'll say modern, but I'm not sure if that's the word I mean,

00:33:48.060 --> 00:33:53.820
but yeah. So we, as an industry, we seem to have moved away from the forms of old and form data to

00:33:53.820 --> 00:33:57.980
send in JSON all the time. Right. And a court schema is about saying,

00:33:57.980 --> 00:34:03.180
I expect this structure to arrive, validate it for me. So it's a very similar,

00:34:03.180 --> 00:34:06.300
it's very similar to FastAPI, which I'm sure a lot of people know.

00:34:06.300 --> 00:34:12.700
Okay. That's pretty interesting. So like you say, very much like FastAPI, it kind of brings in

00:34:12.700 --> 00:34:17.580
some frameworks called model binding, where you can say there's an argument and it's of this type,

00:34:17.580 --> 00:34:21.820
and then, you know, like a class, pedantic class or something along those lines. And it'll,

00:34:21.820 --> 00:34:27.740
here's a data class and it'll take all the inputs, right? Query strings or route primers,

00:34:27.740 --> 00:34:31.820
whatever, and parse them and map them in there. And then just give you an object and go here,

00:34:31.820 --> 00:34:36.860
it's ready. Yeah. So for this library, it supports, I've used all the data documentation as data class,

00:34:36.860 --> 00:34:41.420
but it's pedantic based at the moment. And because of the, it's not actually the typing. So there's no

00:34:41.420 --> 00:34:46.380
dependency injection or anything like that with FastAPI, but it uses the decorator validate request,

00:34:46.380 --> 00:34:52.060
and you pass it the model. And if the request that arrives can be turned into that structure and

00:34:52.060 --> 00:34:57.740
validated as such, then your function will be called and that'd be passed in as data. If it can't,

00:34:57.740 --> 00:34:59.740
then the client will get a 400 response.

00:34:59.740 --> 00:35:00.860
Bad request, bad data.

00:35:00.860 --> 00:35:01.660
Exactly. Yeah.

00:35:01.660 --> 00:35:07.580
Okay. What's the, so validate request makes sense to me. It means like, here's your structured object

00:35:07.580 --> 00:35:11.020
that comes in, passed in as data. What's validate response?

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

00:35:18.220 --> 00:35:21.900
then there's no validation to do, right? You've already said it's the right structure,

00:35:21.900 --> 00:35:26.860
but often you'd return a dictionary or something like that, right? And expect it to go back as

00:35:26.860 --> 00:35:32.140
JSON. What this will do is it will say, I'll check that dictionary is of the right structure. And if it

00:35:32.140 --> 00:35:37.740
isn't, it throws a 500 response to the client and an error that you can go and view in whatever

00:35:37.740 --> 00:35:42.300
error monitoring tool you've got. Sure. Okay. That's excellent. So I see you have data classes

00:35:42.300 --> 00:35:46.540
here and you said it also works with Pydantic. Yeah. So, well, actually a shout out to one of

00:35:46.540 --> 00:35:51.100
your previous recent streams is I'm working on msgspec support at the moment, because I

00:35:51.100 --> 00:35:56.300
actually quite like that. So msgspec is interesting. So it will support Pydantic and message

00:35:56.300 --> 00:36:01.420
spec. So you'll be able to use Pydantic data classes, atas, msgspec stuff, or the message

00:36:01.420 --> 00:36:04.860
spec struct. I think that's probably all of it, but yeah, quite a bit of choice.

00:36:04.860 --> 00:36:10.140
The struct classes really, they've done some super interesting things here to make these

00:36:10.140 --> 00:36:14.780
data classes almost better for performance than even regular and for memory and stuff

00:36:14.780 --> 00:36:18.220
than regular Python classes. It's quite impressive. Yes, absolutely. Yeah.

00:36:18.220 --> 00:36:23.020
Yeah. Just for people who don't know, maybe just tell them real quick what msgspec is.

00:36:23.020 --> 00:36:27.580
They probably know data classes and Pydantic. Yeah. So much like data classes, and you can see here,

00:36:27.580 --> 00:36:33.500
it's a nice, simple way of creating a class with various things created for you. But it also,

00:36:33.500 --> 00:36:39.020
much like Pydantic, will do validation when you try and, if you use one of its functions to convert

00:36:39.020 --> 00:36:45.020
raw data like a dictionary into this type. And much like Pydantic as well, it also gives the ability

00:36:45.020 --> 00:36:51.580
to take this model and turn it into a open API JSON schema definition. And that's the final thing

00:36:51.580 --> 00:36:58.460
Quartz schema does is once you've decorated all your routes, it will auto-generate a open API definition

00:36:58.460 --> 00:37:05.580
for you as well. Oh, it will. Okay. Yeah. Using Swaggers/docs. I think that's really nice. A lot of APIs

00:37:05.580 --> 00:37:09.340
just don't. They're like, we're not going to do that. Yeah. If they don't have something automated to do it.

00:37:09.340 --> 00:37:15.260
And having typed classes representing all your inbound and outbound data, pretty easy, right?

00:37:15.260 --> 00:37:19.660
Yeah. I mean, it really helps with myPy, right? Because instead of having like your request.data or

00:37:19.660 --> 00:37:25.020
something like that, which is some dict that as far as myPy concerns can be anything, it must be a to-do

00:37:25.020 --> 00:37:29.260
instance in this function now. So yeah, really helps with your type checking.

00:37:29.260 --> 00:37:36.060
Yeah, absolutely. All right. What do we got next? Sock from Miguel Grimberg. Flask WebSockets.

00:37:36.060 --> 00:37:40.220
This one, there is an extension for because this is one of the areas that Quart being natively

00:37:40.220 --> 00:37:45.900
async can just do itself. So the Quart, I think I described sometimes as a superset of Flask.

00:37:45.900 --> 00:37:47.740
Because it can do everything Flask can do.

00:37:47.740 --> 00:37:49.500
The C++ of Flask.

00:37:49.500 --> 00:37:51.180
Yeah, exactly.

00:37:51.180 --> 00:37:58.140
Awesome. So WebSockets, a problem that websites generally have not been able to do. Like once

00:37:58.140 --> 00:38:02.460
you make a request, usually there's the response and that's it. But what if the server, you want to

00:38:02.460 --> 00:38:07.180
send more stuff to the server, everyone wants to send stuff out to you later. Like I started this job,

00:38:07.180 --> 00:38:12.700
three seconds later, now it's done. Or this bidirectional exchange is really cool. But a lot

00:38:12.700 --> 00:38:18.060
of times I feel like it's a little over the top. Like a lot of times I think people maybe just want push

00:38:18.060 --> 00:38:23.420
notifications from the server and not true. Like let's have a conversation. I just need to know

00:38:23.420 --> 00:38:27.180
when this happens. Just let me know as soon as it's done. Right. So for that, yeah, go ahead.

00:38:27.180 --> 00:38:32.860
Server sent events, sorry, seem to be not that well known, but yeah, probably described your usage

00:38:32.860 --> 00:38:37.900
really well. That's exactly what I was getting at. So does Quart support SSE? Does it support server sent

00:38:37.900 --> 00:38:44.540
events? Yeah, yeah. I think there's an extension for it, but in reality, it's a very small bit of code

00:38:44.540 --> 00:38:51.100
you need. So it's yeah. I mean, cause you, you literally return a response with a certain header

00:38:51.100 --> 00:38:56.060
and then you just return a, well, here we go. Here's the, there's an example in the documentation

00:38:56.060 --> 00:39:00.060
for Quart, but you just return a certain data structure and then you, you've got it. Yeah.

00:39:00.060 --> 00:39:04.860
Yeah. So I think that's when people say they want web sockets, I think most of the time,

00:39:04.860 --> 00:39:09.020
this is what they want. They want the client, the server to call the client sometimes, not just the

00:39:09.020 --> 00:39:13.420
other way around. Yeah. So I'm going to go slightly off topic because it's interesting,

00:39:13.420 --> 00:39:17.820
but it's related to this, but one of the things I implemented in Hypercorn, because I find it

00:39:17.820 --> 00:39:23.420
really interesting is that I think two, no, it must be more like four years ago, they introduced web

00:39:23.420 --> 00:39:28.140
sockets for HTTP two. And of course, one of the reasons you want web sockets because the cost of

00:39:28.140 --> 00:39:32.940
opening the connection for HTTP one is, is fairly expensive. So you can keep it open and do the bi-directional.

00:39:32.940 --> 00:39:38.140
But with HTTP two, you don't even need an extra connection anymore. You can have your web socket stream on the

00:39:38.140 --> 00:39:42.540
same connection as all your requests as well. So it's really nice and efficient. So yeah,

00:39:42.540 --> 00:39:46.220
I'd love to have an opportunity to make more use of it. So I find it really interesting, but

00:39:46.220 --> 00:39:50.060
the code I write professionally at the moment doesn't quite need that level of real time interaction.

00:39:50.060 --> 00:39:53.900
I know, I know all the stuff I built, I'm like, that would be so fun, but you know,

00:39:53.900 --> 00:39:55.980
I just don't need that. Yeah, exactly.

00:39:55.980 --> 00:40:02.300
I have no use case. I mean, there are apps I'm sure that do it, right? Like really complicated

00:40:02.300 --> 00:40:07.260
single page apps like StreamYard that we're using right now. For example, this has probably got some

00:40:07.260 --> 00:40:13.340
crazy interchange going on, but I don't write this kind of apps either. All right, let's see. So

00:40:13.340 --> 00:40:18.940
the socket stuff, which is cool. That's like you said, built into court. That's one of the advantages

00:40:18.940 --> 00:40:24.540
of it. But you know, you can use Miguel Grimberg's stuff to add it to Flask. SQLAlchemy, you mentioned

00:40:24.540 --> 00:40:30.700
them before, probably outside. If you're not in the Django space, it's not the only option for sure,

00:40:30.700 --> 00:40:35.900
but it's probably the de facto choice people when they think, I want to know our ORM for a relational

00:40:35.900 --> 00:40:41.260
database. You probably start here, right? Yeah, I think you would. Yeah. And this is a port,

00:40:41.260 --> 00:40:47.740
as I understand it, of the Flask, SQLAlchemy extension to work with or natively work with court. So

00:40:47.740 --> 00:40:54.300
should match all your expectations, I think. And if you're used to it, you just use this and it works

00:40:54.300 --> 00:41:00.380
with court and there you go. Let's see an example here. You can set up your database with a pretty

00:41:00.380 --> 00:41:06.780
interesting nested type of construct here, like just, you know, passing rich arguments that take

00:41:06.780 --> 00:41:12.540
more. And if you look at the code on there, just the shape of the code is kind of unique here, but

00:41:12.540 --> 00:41:19.020
you end up with this constructed database thing and then it has the base model class and off you go,

00:41:19.020 --> 00:41:23.740
right? Just get a session from it, do all the things. I've always had mixed feelings about

00:41:23.740 --> 00:41:28.060
these things that you set up here that are built like into the web frameworks,

00:41:28.060 --> 00:41:31.500
essentially that make them part of the request. Because if you're going to go and write little

00:41:31.500 --> 00:41:36.940
scripts that also do data stuff, you want to want to have, be able to get access to the data and the

00:41:36.940 --> 00:41:40.940
models over there as well. All those looks like it does take the app there, but I don't know if it

00:41:40.940 --> 00:41:45.900
really needs a meaningful app. Like, I don't think that it would use it probably, but it just adds it.

00:41:45.900 --> 00:41:51.340
No, I don't think you need to be within an app context to use the DB. I may be wrong there,

00:41:51.340 --> 00:41:54.140
but yeah, I think you could probably use it in a script. Yeah.

00:41:54.140 --> 00:41:57.740
Yeah. That's the only thing that concerns me about these like database helpers is like,

00:41:57.740 --> 00:42:02.860
you know, I need to do database stuff outside of a request as well. Even potentially some of the stuff

00:42:02.860 --> 00:42:06.620
that we're going to talk about in a minute, like it's just a background task that's not part of an

00:42:06.620 --> 00:42:10.460
actual request, but it could even still be in the web server process, right?

00:42:10.460 --> 00:42:16.540
I use, there's an extension called court DB, which is my preference because I prefer to write the SQL

00:42:16.540 --> 00:42:23.340
and go down the OIM route. And yeah, that works as you'd expect. You'd still set it up and pass it

00:42:23.340 --> 00:42:28.140
the app, which I think is true of this, but then you can just use it without an app context. It just

00:42:28.140 --> 00:42:32.460
basically uses the app to get the configuration values. So it knows where the database is.

00:42:32.460 --> 00:42:37.260
Interesting. And now we're on to probably the biggest one that you want to talk out,

00:42:37.260 --> 00:42:43.500
talk about, which is quite exciting, court tasks. Yeah. So this is a very recent release that I

00:42:43.500 --> 00:42:49.500
haven't announced anywhere else yet. This is something I think I read a post recently that said,

00:42:49.500 --> 00:42:55.100
of free web framework, you need like five things. Like you need a database connection. You need some

00:42:55.100 --> 00:43:01.740
kind of validation, input, output, rate limiting, authentication, and you need array to run periodic tasks.

00:43:01.740 --> 00:43:08.140
And in the past, my way to do this was extra infrastructure. So it was cron running somewhere,

00:43:08.140 --> 00:43:13.660
or some kind of, I think cloud watch does some kind of event generator based on a cron schedule.

00:43:13.660 --> 00:43:18.540
And for me, that's just annoying because now I have to build extra infrastructure,

00:43:18.540 --> 00:43:23.340
which I don't need. I'm not at that scale. Yeah. Maybe you got celery or rabbit MQ or something

00:43:23.340 --> 00:43:27.980
like that, right? Yeah, exactly. But again, with those, you need that extra infrastructure for it to work.

00:43:27.980 --> 00:43:31.580
Yeah. It's another server. It's another thing that could get hacked. You got to patch it.

00:43:31.580 --> 00:43:34.860
You got to secure it. You got to know how to run it. It could go down and then

00:43:34.860 --> 00:43:39.500
none of your background stuff works anymore. It's all, it's all a hassle, even though it has a benefit.

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

00:43:45.100 --> 00:43:49.100
the background periodically. And obviously at some point in this one, normally on scale,

00:43:49.100 --> 00:43:53.100
because I have so many background tasks that dominate the server and stop it doing requests.

00:43:53.100 --> 00:43:56.780
But while I'm small and starting out, this is just makes life so much easier.

00:43:56.780 --> 00:44:00.940
Yeah, absolutely. It totally does. And because everything's async and await, you can just set up

00:44:00.940 --> 00:44:07.580
the task and just await the task. And it just blends in hopefully well with all the web requests, right?

00:44:07.580 --> 00:44:13.980
Yeah. Yeah. So for court tasks, if you write your task as an async def, then it will run it in the event

00:44:13.980 --> 00:44:19.660
loop. If you write it as a def, it will run it on a thread. So if you have synchronous code that's going

00:44:19.660 --> 00:44:25.420
to block the event loop, like you're using traditional IO to want for one of the better phrase, then if you just

00:44:25.420 --> 00:44:30.940
use def, they will run it on a task, won't block your event loop and you'll be fine. So yes, it's nice in that respect.

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

00:44:37.420 --> 00:44:42.140
Yeah. Yeah. It's just if you run a whole bunch of synchronous code or something like that, you might

00:44:42.140 --> 00:44:48.140
start to clog things up, but don't do that. Honestly, like what you find, what I find anyway on the web

00:44:48.140 --> 00:44:52.860
most of the time is you're talking to other things, talking to a database, talking to a cache, talking to

00:44:52.860 --> 00:44:59.420
an API, and all those things are like perfectly suited for await because it's most of the time you're

00:44:59.420 --> 00:45:03.980
almost outside the web app. Yeah. I think the perfect one for this that I keep thinking of is

00:45:03.980 --> 00:45:09.980
when you want to send a periodic email, like a reminder or a summary of the day, just write this

00:45:09.980 --> 00:45:14.780
decorated task run and you have your function that just sends the emails. It's yeah, that's why it

00:45:14.780 --> 00:45:18.460
exists for me. It makes that so much easier. Yeah, absolutely. Right. Yeah. Like you say,

00:45:18.460 --> 00:45:22.460
just send out a quick email like, "Hey, I need to reset my password." All right, here, we're going to send

00:45:22.460 --> 00:45:27.660
you a call on API, like SendGrid or something like that. We're going to send you, but you don't want to block

00:45:27.660 --> 00:45:31.660
block things up. You just kick that onto the background work and off it goes.

00:45:31.660 --> 00:45:36.620
So one of the things that people would say, all right, you guys, I know you're excited about

00:45:36.620 --> 00:45:43.740
async, but we need durability. What about, what if the server goes down or what if another potential

00:45:43.740 --> 00:45:49.500
issue is what if you scale, like at least the cron stuff, like what if you ban out your web app into

00:45:49.500 --> 00:45:54.460
like 10 worker processes and you say, "Run in five seconds," and they all run in five seconds,

00:45:54.460 --> 00:45:59.500
10 times or, you know, those kinds of issues here. So.

00:45:59.500 --> 00:46:07.500
So this extension supports a couple of extensions to it. So you can pass it a custom store, which allows

00:46:07.500 --> 00:46:13.500
you to store when the task last ran. And so that means if the server goes down and it misses an invocation,

00:46:13.500 --> 00:46:18.460
then when it comes back up, it will recognize that and run the task straight away, rather than wait till the next time.

00:46:18.460 --> 00:46:25.500
And the other thing it supports is you can decide what should happen before the task starts and what should happen after.

00:46:25.500 --> 00:46:33.500
So this allows you to put some locking in for your second case. In both cases, the reason it's not built in to court tasks is you'll need to decide

00:46:33.500 --> 00:46:38.500
where you store this information, where you store the lock, where do you store the. So usually a database works for that.

00:46:38.500 --> 00:46:41.500
So yeah, so you can add it all, but you've got to choose how yourself.

00:46:41.500 --> 00:46:45.500
That's cool that it has placeholders and ways to plug that in.

00:46:45.500 --> 00:46:53.500
That's what I was thinking. I was right. Like instead of just storing it in memory and throwing it on the queue, put it into the database and have the task just check the database and go, okay, I'm working on it.

00:46:53.500 --> 00:47:00.500
No one else do this. We don't want to send 10 emails to the person. I got it. Right. All that kind of stuff.

00:47:00.500 --> 00:47:10.500
It's not a job queue, although I've been thinking about that recently. So yeah, it can't be like, it doesn't work in the sense that job will run once it will do jobs periodically.

00:47:10.500 --> 00:47:22.500
Got it. Okay. So more of a scheduler type of thing. So I need to see if anyone's uploaded files to this location and then convert it from PNG to WebP or like safe space or

00:47:22.500 --> 00:47:29.500
you could be like every morning, I want to send out an email to the users and say, imagine it's a to do app because everyone uses that as an example.

00:47:29.500 --> 00:47:31.500
And say, these are your tasks for the day or something like that.

00:47:31.500 --> 00:47:36.500
Sure. There are people who've signed up for the daily digest, send them their digest. Right.

00:47:36.500 --> 00:47:37.500
Yeah.

00:47:37.500 --> 00:47:37.500
Yeah.

00:47:37.500 --> 00:47:37.500
Yeah.

00:47:37.500 --> 00:47:46.500
Very cool. And because it's on a background thread, it's not going to harm anything. Excellent. Okay. Well, it looks pretty simple. Again, very decorator driven. Right.

00:47:46.500 --> 00:47:56.500
Yeah. So I mean, with all of these, the extensions at least I've written, I've tried to follow the kind of flask conventions for better for worse. So yeah. Same with court. It follows flask.

00:47:56.500 --> 00:48:17.920
Yeah. It feels like if you're already doing flask or court, you just know what to do. It's just similar. Yeah. Excellent. Well, I think we've covered all the ones we explicitly said, let's make sure we get a chance to cover those. I definitely want to encourage people to check out the court extensions page because there's quite a few more. I'll see if maybe some jump out at me here that are kind of cool. Court B trip.

00:48:17.920 --> 00:48:23.920
We briefly mentioned court DB, which is one I maintain. So I can say a bit more about that if you're so interested.

00:48:23.920 --> 00:48:25.920
Yeah, sure. And it's hanging out right here on your list.

00:48:25.920 --> 00:48:43.920
So this is much like the SQLAlchemy one. It's a way to connect to a database, but unlike the SQLAlchemy way, it doesn't promote an ORM rather it just gives you connection that you can run SQL on. I don't know you can do that with SQLAlchemy. I just, I mean, typically that's not what people do.

00:48:43.920 --> 00:48:56.920
So in this case, like the example here is you, you pass it the URL you want to connect to and it supports Postgres and SQL light at the moment. And then in the roots, you have this G dot connection object that you can make use of.

00:48:56.920 --> 00:49:07.920
And you can make use of that in background tasks. And it's quite easy to make use of it actually in court tasks as well. It's just a little snippet you need to do. But yeah, and that's it. You get a connection that you can do whatever you want with.

00:49:07.920 --> 00:49:22.920
Yeah. Okay. Very nice way to just have everything set up right. Like you don't have to worry about passing things around and create a transaction. Go do your things. Right. Excellent. We got court events for broadcasting server sent events and web sockets. That's pretty cool.

00:49:22.920 --> 00:49:28.920
There is one. Yeah. Yeah. Another one that's cool is, minifying all of your elements, right? CSS JavaScript.

00:49:28.920 --> 00:49:51.920
I've been doing that at the build stage a lot recently. There's a little snippet I can share if people want for court where you change the static function. So it will serve a G zipped version of the static file over the un-G zipped one if the client will accept it. And yeah, then if you do it build time, cause I think this one might be a mistake, but I suspect it works at runtime. So it compresses at runtime.

00:49:51.920 --> 00:49:58.140
Sounds like it does. Yeah. It talks about doing it as a response, right? So yeah. Oh, it does have a cache though.

00:49:58.140 --> 00:50:01.960
Ah, okay. Yeah. Well, you get similar. Yeah. Very similar to what I'm suggesting then.

00:50:01.960 --> 00:50:09.500
Excellent. Now those are really, really handy. If people go and they want to make their website better, check out, I guess, page speed.

00:50:10.340 --> 00:50:34.020
Lighthouse is what I was going to call it. Yeah. But they renamed it to page speed. So you can go put in and talk Python and see how we're doing, but it'll give you things like how is your site fast enough? Let's see how the desktop one's doing right. It'll give you a score. And you know, are we going to index you well, or are you going to be punished for having some kind of crappy experience in this minification stuff that we're talking about can help there. Right?

00:50:34.140 --> 00:50:50.600
Absolutely. Yeah. Similar to your DB, your QuartDB, we have QuartMotor, which is the async database access library for MongoDB. So I suspect it's super similar. I haven't used it because I use Beanie, which is like ORM style. So kind of on top of those things.

00:50:50.600 --> 00:51:02.020
KeyCloak is interesting. Redis also is pretty interesting. Uploads. Let's finish it out with uploads. We'll call it after that. So I haven't used this, but uploading files can always be a bit of a hassle, right?

00:51:02.020 --> 00:51:12.520
It looks very similar to the Flask uploads one. So it's a, it's kind of a nice wrapper around how you get access to the files you've uploaded and what you do with them kind of thing. Yeah.

00:51:12.520 --> 00:51:23.780
Yeah. That can be a little bit of a hassle, right? You got your request.files and all that. Don't know how to use this thing, but I know that people kind of struggle uploading files a lot. So that can be a thing.

00:51:23.780 --> 00:51:28.840
I don't tend to actually write it that much come to think of it, but yeah, it's just makes it easier.

00:51:28.840 --> 00:51:43.040
Yeah, exactly. You know, profile, profile pictures, maybe it's like that kind of stuff I'd be doing. Anyway, I guess I've wrote some code that lets you upload MP3s at one point for podcasts, but I stopped using it. So it's not top of mind anymore.

00:51:43.040 --> 00:51:51.360
Anyway, excellent stuff here. I love just going through and seeing all these little pieces, these little building blocks you can bring together and just quickly build an app.

00:51:51.460 --> 00:51:54.860
So thanks for being on the show and telling everyone about it.

00:51:54.860 --> 00:52:00.160
Oh, thank you. Thank you. Yeah. Great. Hopefully opportunity to introduce some stuff that I hope people will find useful.

00:52:00.160 --> 00:52:03.820
Yeah. And just more broadly, like thanks for working on Quart and pushing that forward.

00:52:03.820 --> 00:52:13.940
Yeah. I mean, like I said, our aim is hopefully we'll, well, we're kind of going around in different ways of how we'd actually might merge Flask and Quart, but that's still the ultimate aim.

00:52:13.940 --> 00:52:20.940
I kind of think because of the limitations we were talking about earlier about running async code when you're deep in async code base.

00:52:21.280 --> 00:52:30.980
I think you'll always have to make a choice with Flask where if you're going to be mostly sync, you go Flask. If you're mostly async, you go an async Flask or whatever it's called, which at the moment it's called Quart.

00:52:30.980 --> 00:52:44.600
We might be able to put them in the same code base perhaps. So then you can, you know, maybe mix and match because you can, you can certainly run Flask and Quart together with Hypercorn as the server and just have a, what's called the dispatcher middleware layer in between.

00:52:44.740 --> 00:52:52.380
That says for these routes, it goes to Flask and for those routes, it goes to Quart. So yeah, there are lots of ways to do it, but it depends what you want to do really.

00:52:52.380 --> 00:52:58.560
Yeah. That's interesting. Maybe you could even create a different app instead of app equals Flask. It's app equals async Flask. Who knows?

00:52:58.560 --> 00:53:03.280
That's effectively what Quart is. I mean, I could, if I called it async Flask at the start.

00:53:03.280 --> 00:53:08.820
A Flask. Yeah. It ranks higher in the alphabetical sortings.

00:53:08.980 --> 00:53:17.700
Yeah. All right. So people are excited with Quart. Maybe it's back on their radar now that we've been talking about all these extensions. What do you tell them? How do they get started? What do they do?

00:53:17.700 --> 00:53:30.640
Oh, I mean, you can just pip install Quart and a quick start is as simple and familiar as it's ever been for Flask. You just do app equals Quart and then app.get or app.whatever and then your function below to handle it.

00:53:30.640 --> 00:53:35.640
Yeah. The selling point is basically if you know Flask, you know Quart, you just use the word async some of the time.

00:53:35.640 --> 00:53:46.300
Yeah. Yeah. Yeah. I mean, even the WebSocket support, I've tried to make it as familiar as it would be if you knew Flask. So instead of using the global request, you now use the global WebSocket.

00:53:46.300 --> 00:53:52.740
So there's WebSocket.headers and you can do WebSocket.send, send a message, that kind of stuff. So hopefully it just feels familiar.

00:53:52.980 --> 00:53:57.500
Yeah. Fantastic. I'm sure it does. And thanks for being on the show and nice to have you back.

00:53:57.500 --> 00:53:57.900
Thank you.

00:53:57.900 --> 00:53:58.360
Catch you next time.

00:53:58.360 --> 00:53:59.080
Yeah, you bet.

00:53:59.080 --> 00:54:07.180
This has been another episode of Talk Python To Me. Thank you to our sponsors. Be sure to check out what they're offering. It really helps support the show.

00:54:07.180 --> 00:54:16.120
This episode is sponsored by Posit Connect from the makers of Shiny. Publish, share and deploy all of your data projects that you're creating using Python.

00:54:16.120 --> 00:54:22.680
Streamlit, Dash, Shiny, Bokeh, FastAPI, Flask, Quarto, Reports, Dashboards and APIs.

00:54:23.480 --> 00:54:30.760
Posit Connect supports all of them. Try Posit Connect for free by going to talkpython.fm/posit, P-O-S-I-T.

00:54:30.760 --> 00:54:36.600
Want to level up your Python? We have one of the largest catalogs of Python video courses over at Talk Python.

00:54:36.600 --> 00:54:41.780
Our content ranges from true beginners to deeply advanced topics like memory and async.

00:54:41.780 --> 00:54:47.360
And best of all, there's not a subscription in sight. Check it out for yourself at training.talkpython.fm.

00:54:47.360 --> 00:54:52.240
Be sure to subscribe to the show, open your favorite podcast app and search for Python.

00:54:52.240 --> 00:55:02.920
We should be right at the top. You can also find the iTunes feed at /itunes, the Google Play feed at /play, and the direct RSS feed at /rss on talkpython.fm.

00:55:02.920 --> 00:55:05.880
We're live streaming most of our recordings these days.

00:55:05.880 --> 00:55:13.660
If you want to be part of the show and have your comments featured on the air, be sure to subscribe to our YouTube channel at talkpython.fm/youtube.

00:55:14.320 --> 00:55:18.220
This is your host, Michael Kennedy. Thanks so much for listening. I really appreciate it.

00:55:18.220 --> 00:55:20.120
Now get out there and write some Python code.

00:55:20.120 --> 00:55:20.460
Thank you.

00:55:20.460 --> 00:55:26.260
Thank you.

00:55:26.260 --> 00:55:27.260
Thank you.

00:55:27.260 --> 00:55:28.260
Thank you.

00:55:28.260 --> 00:55:34.060
Thank you.

00:55:34.060 --> 00:55:34.060
Thank you.

00:55:34.060 --> 00:55:40.860
Thank you.