Have you heard of Quart? It's the fully async version of Flask created by Philip Jones,
who's working closely with the Flask team on these parallel projects. The TLDR version is
that if you want to take advantage of async and await and you're using Flask, you want to give
Quart a solid look. We've spoken to Philip previously about Quart. This time around,
he's here to share his top Quart extensions and libraries that you can adopt today.
This is Talk Python to Me, episode 452, recorded January 10th, 2024.
Welcome to Talk Python to Me, a weekly podcast on Python. This is your host, Michael Kennedy.
Follow me on Mastodon, where I'm @mkennedy and follow the podcast using @talkpython.
both on fosstodon.org. Keep up with the show and listen to over seven years of past episodes
at talkpython.fm. We've started streaming most of our episodes live on YouTube. Subscribe to our
YouTube channel over at talkpython.fm/youtube to get notified about upcoming shows and be part of
that episode. 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. Streamlit, Dash, Shiny,
OKFastAPI, Flask, Quattro, Reports, Dashboards, and APIs. Posit Connect supports all of them. Try
Posit Connect for free by going to talkpython.fm/Posit, P-O-S-I-T. And it's also brought to you
by us over at Talk Python Training. Did you know that we have over 250 hours of Python courses? Yeah,
that's right. Check them out at talkpython.fm/courses. Philip, welcome back to Talk Python.
Tommy. Great to have you here. Thank you. Thank you for the invite. We're going to talk about web
things. I love some web things. We're going to talk about async. I love async. And we're going to talk
about a lot of little cool things you can plug into your web apps that make them more awesome.
And we give a lot of people a lot of things to think about. So yeah, let's do it. So I guess
quickly before we get into talking about Quart, maybe a little catch up on where it's been since the
last time we spoke about that. Give folks a bit of background on yourself who don't know you.
Sure. Yeah. I guess probably in this context and maybe the people listening are probably known
for Quart and Hypercorn and helping maintain the pallets libraries like Flask and Virksuk.
But well, outside of that, I guess in my professional career, I work as a software engineer in London.
I work for a company called Curaleaf International. And 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. Yeah. Put that aside. So I've worked there for about a year and a bit now. Before that,
worked at various other software firms. And then before that, I was actually a particle physicist
working on an experiment out in Canada in a deep mine, which was quite a good fund.
So you like your variety of your jobs, I can tell.
Yeah. Had a definite career shift.
Indeed. Just a funny sidebar, you know, cannabis was legalized here three or four years ago in Oregon.
There are more cannabis shops than there are Starbucks, than there are McDonald's. Like
I could walk to four from my house in five minutes. It's nuts. Anyway, it's very different than it was a
few years ago, for sure. It's all fine, but just different. All right. So court, like Flask,
kind of, but very interesting. It's got some other capabilities. Tell people about court. How's it
relate to Flask? There's a lot of crossover. You and David Lord are working pretty closely,
it seems like, on things these days.
I started court about, I guess, seven, maybe a bit more years ago now. And the original idea was
make Flask async. At the time, I couldn't figure out how to adapt Flask at all in that direction. So I
thought, okay, I'll start by just re-implementing the Flask API using async await. And that's what
court started out to be. Over the years, David and I have worked to try and merge the two together.
So the very latest change that's happened a few months ago is we actually based court on Flask
now. So there's a lot of shared code now. And for a long time, court was based on VirgSug. And if
you're familiar with the palettes and Flask ecosystem, Flask is built on VirgSug. And court was,
but now court is built on Flask, which is built on VirgSug. And they're very, very similar.
Yeah. Oh, that's fantastic. Because the more that you can share,
like the more you all can just focus on features and making it better rather than duplicating that
effort.
The other aim is that the APIs are the same as they can be other than async await. So you
can take your Flask code base and move to court just by adding async await. That's the dream,
even closer than it's ever been to that now.
You're pretty close to be able to rename the word Flask, keeping the case sensitivity
intact from Flask to court. And it works, right?
Yeah. In simple projects, yeah. The more complicated ones, it comes down to the extensions,
which we're going to talk about.
Yeah. That's the whole topic of today, right?
Yes, exactly. But yeah, that's the aim. Our long-term aim is to actually merge the two
and be able to say async Flask. Well, it's kind of the case now where you can say async Flask is court
and we can't quite do what Django has done and put async Django as it is in Django. Ours,
you have to make a naming choice from the start. But yeah, I think it's getting as close as we can get.
Maybe we'll figure out how to merge them in the future.
Yeah. It'd be great if you could choose to use async def or just regular functions for your views
and not really think about it.
You can't do that.
Okay.
You've been able to do that for about two years, I think, trying to figure out when we merge that.
So you could use either. It's just not as performant. If your code base is going to be
mostly async, you probably want to switch to court fully because it'd be more performant
and more reliable and robust.
Sure. Why is that?
The Flask implementation will run your async code on a separate thread.
So you get all that overhead of switching between the threads, sending the information over and back,
et cetera.
Right. Does it run like an asyncio event loop on the other thread, but then
basically curry the results back and forth?
Yep.
And with court, it's running right on top of an ASCII server straight, right?
Yeah.
Yeah. That's super cool. And you might be responsible for one of the more prominent ones out there.
Tell people about, well, what is an ASCII server for people who don't know?
Why does it exist? And then, you know, the one that you got.
Yeah, sure. So historically there was the whiskey standard, which separated servers from
frameworks applications and kind of gave you a choice between how do you want to serve
and deal with concurrency and stuff? And then how do you want your API to build your apps,
the framework to be? I think it was a great choice because it made the ecosystem really,
really nice for developers. And ASCII is the same thing for async. And so court is a,
is an example of an ASCII framework and Hypercon is an ASCII server.
They originally together, much like Sanic started out as well, but then I split that out Hypercon
into its own thing. And then later on now, Hypercon is also a whiskey server. So yeah,
in some respects, Hypercon is just a general purpose server for Python.
And so Hypercon, it lives in the same general usage base as G Unicorn and MicroWSGI UWSGI,
right. So I could run it in production in a kind of like a overseer mode where you can fan out
process, worker processes and system daemon, system D, all that, that, that where it's at.
Yeah, it's exactly like that. Yeah. Yeah. And it also in the ASCII space, it's similar to UVcorn or
Daphne. That's it. Right.
I think there's another, but I've forgotten the name just now.
Yeah. I was just messing with some Docker stuff and I was running the web app, a FastAPI app,
NG Unicorn using UVicorn workers. So handling the ASGI stuff that way, is it better to use Hypercorn?
Is it rather than something along those lines, a little more direct maybe, or what do you think?
I think it's a bit of a choice. So how it differs from UV, well, UVcorn in general is quicker because
it's HTTP parser. HP1 parser is quicker. And there's a few other choices that tend to make it a bit
quicker. However, this last time I checked, UVcorn doesn't support as many extensions as Hypercorn and
it doesn't support HTTP2 or HTTP3. Now, most people don't actually serve that directly. They tend to have
a load balancer in front, but I tend to do that because it's quite exciting. So you're getting there
with Hypercorn. And then after that, it's a bit of a choice between, you know, what features do they
both have? What do you want to use? So they're fairly similar, I would say.
Sure. Okay. Somewhat. Up to you, what do you want to choose? But for people who have mental models,
maybe using UVicorn, they can check out Hypercorn, right?
Yeah. Yeah. So you wouldn't need G Unicorn because Hypercorn has the process model that G Unicorn adds to
UVicorn. And yeah, you can just switch over. It should be a one-liner because most of the time it's just
that one command line where you set this up, right? So yeah.
Yeah. Very exciting. I'm going to give it a solid look because I'm deep in that world right now,
playing with things, which is very exciting, but not the topic for today. So the topic for today is
people who are interested in doing Flask and Quart, especially Quart, what are ways you can do more,
right? So the frameworks are pretty simple, right? That's of all the different APIs out there,
the Flask API has definitely won. You know, even though there's many different web frameworks,
right? If you look at the way programming works, you look at FastAPI, you look at a Litestar,
many of these things, it's like you've got to create a thing called app and then you say app.get and so
on, right? So this style of programming is really popular. And I think it's because it's just
straightforward and simple. Opposite of Django, maybe. Not so many batteries included, but you can
add the batteries through extensions. Yeah.
Yeah, exactly. I think that's always been the trade-off between Django and Flask and perhaps
even Django and Quart if you were to go Quart is Django makes the choice of your batteries to a large
extent, whereas Quart and Flask means you have to choose basically. Yeah.
Yeah. Which is really nice if you got like, you know, like I would really rather use this graph
database for this thing. And I would really rather, you know, like you get a lot of choice, but you
have to, you're going to have to build a little bit more yourself and you've got to decide more
yourself. So, but with all these extensions, it starts to become easier to click the Lego blocks
back together. Yeah.
Yeah, exactly. Yeah.
All right. So if you go, people listening, go to court.palettesproject.com and go in there and find
the extensions. There's a great long list of extensions that we've got here that they can
choose to run, right? And how to do that and so on. But you picked out a couple that you really want
to highlight and we'll dive into them. And if there's extra time, maybe we can, you know, pick some
more out of the list and just see what we can make of them.
I think the best one to start with is the bit of discussion around an extension called
court flask patch, which is a bit of a odd name and maybe need to think of a better name
for it. But the idea behind this extension is it patches court at import time to look very
much like flask. So you can use some flask extensions directly. So this is again, the idea
to make it easier. If you are so willing to transfer from flask to court, this would allow you to
an easier step because you could add this one line and court flask patch and some of your flask
extensions would just work. Now you've changed the court.
That's awesome because there's a bunch out there. So what happens when I run this? Does it basically
say like, okay, there's a bunch of module namespaces called court this court that let's create proxies
called flask that just delegate into the court elements and expose that back. So code written
against flask dot whatever think it still exists. Yeah, exactly that. Yeah, it does slightly more because it
what it also needs to do is because a lot of the court APIs are async. It needs to wrap them in a wrapper
that allows it to be called synchronously, but it needs to do so in a nested sense. And I don't know whether
you've looked much into like nesting event loops in one another that you're getting back to the same event loop.
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.
Yeah. I really wish that Python had an ambient event loop. That was the default. This like,
get me an event loop. Oh, it doesn't exist in exception. Oh, create an event loop. Sorry, exception. It already
exists. Like, you know, there's like, it's just really tricky to figure out kind of deep down in
some library, like, well, am I in charge of this? Is it already going? And I got to be part of something
bigger. If you could just say, just async run this, you know, and just you do that. You need to make an
event loop, make it. If you don't need to make one, just put it in the one that's already there,
right? Like I really wish there was a little bit cleaner story there, but you know, we're not going to
solve that with court extensions. No, no. I mean, if they had have done that, it would have made making
Flask async a lot easier. You've probably come across the library. I think it's Greenback. Greenback. No, this is new.
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
sync code, which is quite nice in the sense that, you know, it allows you to do all the stuff that's
difficult, but it's not so nice in that it's kind of hidden in a way, monkey patching type stuff going on
in the background. Yeah. Yeah. Yeah. I think this is what SQLAlchemy uses to get their async await
supporting. So it's certainly, yeah, I guess popular. Yeah. Sure. Reenter asyncio or trio event loops from
synchronous code. Very nice. Cool. Okay. So Flask patch, that's pretty good if you just have some
Flask code and you're like, oh, why don't you switch to court? But hopefully nothing, nothing changes and
I'll just run this at the beginning and everything will work. How often does that, how often does it
work out to be easy like that? So it depends on what the extension does. So if the extension does its own
IO or calls certain Flask functions that we can't, this extension can't patch, then yeah, it just fails.
And yeah, so not all of them. If you go into tests, you can see an example of some of them that we test
against or I test against, sorry, just to make sure they work. Like login potentially? Yeah. So login is
probably the most popular, I think. So I don't think it works for SQLAlchemy, for example. There's a
technical example, anywhere that uses app context as a, in a contact manager in a synchronous location,
basically that's too hard to patch. So they tend to all fail, but yeah, a few do. And I hope it makes
it easier for people. I'm pretty sure it is used because I get bug reports. So some people find it
useful at least. Yeah, absolutely. It looks, looks excellent, right? So once you, once you do all the,
the magic, you can just work with the login manager and off it goes, right? Yeah, exactly that. Yeah.
Super cool. This portion of Talk Python to Me is brought to you by Posit, the makers of Shiny,
formerly RStudio, and especially Shiny for Python. Let me ask you a question. Are you building awesome
things? Of course you are. You're a developer or data scientist. That's what we do. And you should
check out Posit Connect. Posit Connect is a way for you to publish, share, and deploy all the data
products that you're building using Python. People ask me the same question all the time. Michael,
I have some cool data science project or notebook that I built. How do I share it with my users,
stakeholders, teammates? Do I need to learn FastAPI or Flask or maybe Vue or React.js? Hold on now.
Those are cool technologies, and I'm sure you'd benefit from them, but maybe stay focused on the
data project. Let Posit Connect handle that side of things. With Posit Connect, you can rapidly and
securely deploy the things you build in Python. Streamlit, Dash, Shiny, Bokeh, FastAPI, Flask,
Quarto, ports, dashboards, and APIs. Posit Connect supports all of them. And Posit Connect comes with
all the bells and whistles to satisfy IT and other enterprise requirements. Make deployment the easiest
step in your workflow with Posit Connect. For a limited time, you can try Posit Connect for free
for three months by going to talkpython.fm/Posit. That's talkpython.fm/P-O-S-I-T. The link is in
your podcast player show notes. Thank you to the team at Posit for supporting Talk Python.
Cores. Everybody loves their cross-site origin scripting restrictions. I guess we do love it in
the sense that we're not getting our bank account stolen when we browse the web. But as a web developer,
you're like, oh, Cores again. I need to allow this thing to talk to that, but it can be a hassle.
And so I'm guessing Cores allows you to control those settings and expose them and so on?
Yeah, exactly. I think it's become more prevalent, right? Since the switch to kind of front-end
frameworks has been so popular, right? Because you end up serving them, well, typically you end up
serving them separately. But yeah, it's exactly as you described. You can put decorators on roots,
or you can put it on a blueprint or on the entire app, and you just say, this is a Cores policy for
everything. Yeah. Very similar to Flask Cores, but different API.
So you can put it on even like a blueprint and say, this whole section of code is where the APIs live.
Things can call that, but nothing else. That'd be really, that's really nice.
Yeah. Okay.
So it also has a slight web, because obviously Quart does WebSockets where it's Flask,
at least natively can't. But yeah, there's some WebSocket considerations for Quart as well,
which mostly come around to checking the origin header. But yeah, it does that as well.
Okay. So you have a route, and you just put the at route underscore Cores on it, or like you said,
a blueprint or on the whole app or however you want. But, and then what kind of arguments does it take?
What can I ask it, make it do?
Yeah. So I went for the kind of, I guess, functional kind of API for it. So you can put in like the
allow headers or what's the other one, the credentials, I forget what it's called now,
but they're all listed and all keyword arguments effectively to it. There was in the issues,
someone a couple of, maybe last year, put out a new, a suggestion for a new API for Cores libraries
that would be more intuitive, but wouldn't match the actual headers that came out. So at some point,
I hope to like look into that and offer that as well. But at the moment that you basically say
the header and then the values you want. So it's very, you have to really kind of know calls to use it.
Okay. It just basically juggles the headers and there's not a lot of abstractions in between.
No, no. So I think on the, 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.
Sure. So access control, allow origin, access control, allow credentials, expose headers, max age,
all this, all these things that you might, you might want to use, right? Yeah. Very cool.
Yeah. Okay. Oh, you have types as well. So that's lovely. Are you a fan of types, type hints?
Since it came out, I've tried not to write untyped code. I think, especially in a team,
I think it makes a huge difference to the understanding of the code. So yeah, I'm a very big fan.
Yeah. I also think it makes your editors help you more. You know, if it knows what type that is
and you say dot, it'll, is this what you meant? Like, yes, actually, I didn't know that existed,
but that's what I want. Give that to me. Without types, you're kind of like, well, you know,
you could object or weird, useless stuff that you can't do anything with. All right. So cores,
if you need to do cores, there's, I guess, let me know how to do this, but I think pretty much
most of these that exist, there's probably a flask variant. So if you use flask and you're like,
oh, I need cores, but I don't use court, just flask-cores instead of court-cores.
Yeah. Yeah. I think it's quite well maintained, the flask one. So yeah, the flask cores,
it's slightly different API, but the concepts are all the same, obviously. So yeah, not very much.
Another one is auth means so many different things. Is it a session type based auth? Is it OAuth? Is it,
who knows what, YubiKeys? What are we talking here?
Yeah. Session management via a secure cookie. So it's a similar usage, if you're familiar with
flask login, but I didn't want to call it court login because it didn't really do anything with
the login. It didn't check the password or the username or an MFA or anything to do with login,
in my opinion. So I called it auth instead. But yeah, so with this extension, you tell it to log in the
user and give it some kind of identifier and it saves that information securely in a cookie for you.
And whenever the user comes back and presents the cookie, it then pulls it out and turns it into
what is called the current user. So I think it's called the current user in flask login as well. So
very similar to that. All right. So you get this auth user back. What is this auth user? It's not like
out of your database, right? It has properties. By default, it's just as a method to say whether the user is
authenticated and to return their ID. But if you customize it, if you extend it. So what I typically do in my
codebases, I extend it and I add methods to return a model from the database, for example.
Yeah, very cool. Nice. And, you know, there's a lot of fancy login off stuff, but nice session cookie.
You know, it just, this works these days. It still works really well.
It does. Yeah. Much simpler than JWTs and less likely to go wrong. I think.
It's not exactly related, but I've had a lot of websites now I've given up on passwords, but they
don't use something new or they just decided like every time you want to get to the site, you just,
we're going to email you a code and you enter it. When did that become the way we're going to log into
everything? It's driving me crazy. So some good old username password. We're going to store your ID in a
cookie. It warms, warms the heart.
I've been playing with the WebOrph and little USB keys and such recently. I quite like it. I think
it might be a bit complex still. It's a little tricky to get working, but yeah, I think that's
probably the future to be honest with you. The site just sees like, yeah, you got the right
hardware thing. Off we go. Peter knows you're there. Something like that, right? Yeah. Nice. Okay. So
people need to deal with users. This is not about storing them in the database, encrypting, hashing
their passwords or those types of algorithms. It's really just about the web browser exchange that
involves setting a cookie when you're logged in and getting it back and make sure it's not tampered with,
right? Yeah. Yeah. So what it does is it stores the value, like the here, the number two is the ID
for the user. So it stores that in the cookie in plain text, but then cryptographically signs it. So you
don't want to store anything sensitive in the cookie if you're worried about it being read,
but you can store identifiers, et cetera, and know that it hasn't been tampered with.
Yeah. That's the thing. It's not going to harm you to know that your user ID is two or 75117 or
whatever. But if it's two was to stop somebody from going, let me change that to a three and see what
happens. Oh, look, I'm an admin. How cool is that? Right. That's the problem that you're trying to
solve with secure, right? Exactly. Yeah.
Well, from my side, I baked that thing in myself, but having an extension that just does it,
that sounds better. Yeah. So the nice thing, well, the bit I took a bit of interest, I think it's
changed now with Flask Login, but a couple of the areas where I wanted to improve is it does same site
settings on the cookie by default. So it will start with the strict and then you can play around and
go down to lax if you so need. But it also has the special, it allows the special host, you know,
the double underscore host prefixes on the name and stuff like that. So although I don't think in
reality that adds that much to your security, but it's a kind of a safety net in some respects.
Yeah. Sometimes also it becomes a hassle when you're doing development because your host is local host
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
got to have local hosts. And then I don't know, it doesn't feel super hamper resistant to me either.
Are you familiar with SecurePy? This is an interesting project too.
Oh, it's been archived.
Oh, it's archived. Oh, sadly. Is there a new one? No, but I think it still works. I'm not sure what
been deprecated. Okay. Well, I guess that's not being used as well, that are used that much,
but it used to be a way to kind of set all those different things, I believe. Or is this different?
This is a totally different.
Yeah. I think this is, I thought you were going to say secure headers, I think. Is it called that?
Oh, I think it's just called secure what it is. Yeah. That's the one. So sorry. That was some
other random thing that we've done that was called SecurePy, but not, this thing is still alive and
supports court, which is why I brought it up amongst like lots of other frameworks. But yeah,
you were going to tell people about this. This is cool.
Yeah. Yeah. So I pretty much end up adding all of these by hand. So yeah, most of the headers you want
to add. And if you ever do a pen test, right, they're going to, if you don't have these headers by
default, there you go, that's going to be a finding straight away. So yeah, it just adds
all the ones you want really.
Right. For one of the ones that you might not think of that you should probably have,
unless you have, unless you have a really specific use case is what if somebody created a site,
you know, you had service.com and they had service.io or, you know, whatever. And they,
they bought that and then they could put your, whatever their URL is, your, just into your site
as a hundred percent width and height iframe. Right. And then they just capture all the keystrokes
like, huh, you're typing into your thing. It looks like your thing. Your data is there.
You don't want people to do that to your site or just randomly host your site and their site. So
for example, this secure thing will set X frame options to same origin. So you can't embed your site
can no longer be embedded by default. You've got to choose to let it be embedded. All those kinds of
things, right. Are really nice here. Actually got caught out by that many, many years ago. I built
a, an image photo editor in the browser. It started to get quite a lot of juice. I was like,
oh, this is great. And then I realized it was on somebody else's domain, basically in an iframe.
They're like, look at our amazing editor that we're getting Google AdWord money for or whatever.
Right. Yeah, exactly. Yeah. So a little bit of a header action will shut that stuff down,
but I mean, I didn't know to do it until I kind of dug into it more. Right. Or you find out your
site somewhere else. Yeah. I think it's like a lot of it seems to be, yeah, learned with experience,
I guess, isn't it? Or on the job. It only happens once when you see your web app in somebody else's
site embedded as an iframe, right? No, this is not going to be okay. Awesome. All right. So that was
court auth as well as the secure thing. Next, I guess, kind of related to security, maybe not exclusively,
but somewhat rate limiting. Yeah. Cause I mean, on a login route,
you'd want to put a rate limiter on straight away to stop any kind of brute forcing anyway. Right.
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, right? Because if you don't want to be
overwhelmed by them, a lot of times you just say you send them some kind of response code. I don't
know what the status code is like too many requests or something like some 400.
429.
Yeah, there you go. So you can just send them back straight away, but there's no
cost for them to receive that 429. You know, they're just like super quick going. If you wanted to say
like, because maybe they're trying to guess passwords or something, you're like, let's not let them guess
so quickly. So you can like sleep and then respond to them in 10 seconds later. So it's like,
oh, well, I don't know. Is the password good or not? Why don't you wait around for a while
instead of going somewhere else? With a asyncio based website, that's await asyncio.sleep,
nearly zero cost to you, other maybe an open socket. And you can just like send them to sort of time
black holes, super easy. Whereas like a WSGI app, you're blocking a request and you can only handle
so many and all that kind of stuff. Right?
Yeah. So this one sadly doesn't do any of that fun.
It doesn't have punishment built into it. No, but tell us how it works, what it does.
I was going to say, I think there was an article today suggesting you should serve zip bonds or
something to like unpleasant requesters, but yeah, it doesn't do any of that.
Send them like a hundred gigs back.
Yeah. That would be good. That would be good actually.
Yeah. So this one implements rate limiting as it's defined, like a user would be allowed to do so
many requests in a certain time and also implements the RFC for the like headers that you're supposed to
send back when they over, when they go over the rate limits. So they know when they can, or when the client
can then request again. I see like you've made too many, rather than just saying too many requests,
try again later. You're like, and you'll be reset in five minutes or like there's a proper way to communicate that.
The thing I find really interesting about this, which I don't know if everyone does, but I'll say anyway, but
most people implement rate limiting like a leaky bucket or something like that. These are the standard algorithms.
And typically that requires you to store two variables per key per rate limit you're looking at.
But there's a, I think it's not a very well known algorithm called the generic cell rate algorithm. I
think that's right. And this allows you to store one algorithm at one variable per rate limit. And it's
really quite nice. The algorithm is very, when you, when you get it, it's really intuitive and clean. You
basically, what you store is when you expect the next request to arrive. And if the request arrives too early,
then you just rate limit it. And yeah, it's really clever, I think. So I'd like more people to know
about it basically, because it's a really nice solution.
It's a great solution. Yeah. So this, it doesn't slow them down. It just tells them that they've
gone over the limit in this particular one, right? It's not like if you expect it to be in five seconds,
you don't just sleep for four and then let it in.
No, no. So the idea is that this decorator is much less costly in terms of your compute than
your function, your handler. So this decorator will just send back a 429 very quickly and never
touch your root instead. Okay. Interesting. I'm starting to think about maybe if you have
a sink and a wait available and you, your algorithm told you when the next one was coming, you could just
sleep them until the next one's allowed potentially, if it wasn't 10 minutes, right?
That would be the client. I mean, it depends on your rate limits, but there's a good chance
the client would time out, right? So, right. It'd have to be like five seconds or, or something
real short, right? Like I don't want more than one request a second type of rate limit, not like
five per hour that then they would definitely time out. But yeah, there's a lot of, a lot of things you
can do with this. So basically the way it works for people listening is you can set it up on the app.
So the entire app itself, all the requests to it are limited, right? Or you can go and put kind of like
tenacity, you can put the decorator exactly on a particular endpoint and then set the details for
that one, right? Yep. So a bit like cause, you can do it per app, per blueprint or per route,
depending on what you need. Okay. And here you say rate limit one, and then a time delta. What's the one?
It's the count. So you might want, so for a rate of one per second, you might want to express that as
10 per 10 seconds. So it would be instead of the one, the 10, and that allows you a bit of bursting,
if you will. So, you know, that 10 can come in one second or it can be spread over. So yeah,
it allows you to express the rate limits in different ways, really.
Yeah. I think this probably bypasses the static files. Is that true?
If you do the default limits, I think that applies to static files as well. I can't remember now,
but I think that's what it does.
Yeah. Because you might have, you know, six CSS files, 20 images and so on, right? And like
one page request might blast those all back super quick.
Yeah. Yeah. You certainly want a higher limit.
Yeah. But presumably you're using something like Nginx or whatever, or maybe even a CDN. And then
all those other things I described don't actually go through the Python app, ideally.
Yeah. Yeah. I mean, it does work if you do it for the Python app as well. So up to you, I think.
Yeah.
Indeed. All right. WT forms. Next up, court WTF. What is this?
So this was, I'm tempted to say it's been a bit less popular recently with the front end frameworks
and stuff. But when you did template rendering, you'd render the form, then you do the validation
in your Python framework for that form and you pass back the errors. And basically it would,
the WTF framework would do a lot of that for you. So you could just put in like in your template,
an input box and it would do the errors and do the, like you can see here, you could do the CSRF tokens
and render all the HTML for the labels and the actual input boxes and get it all hooked up very
easily. And that's what it does. Yeah. It's a, it does all that. And the validation when it comes
to the backend as well, I think you can just do form dot validate on submit or something like that.
Yeah, there we are. And that's it. That's your whole validation. So it saves a lot of effort, really.
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 all you like.
Yeah, I think it's, I can't
remember exactly. I think WTF might have hooks so you can put styling directly in, but yeah,
it's, it's pretty, yeah, pretty generic as I remember it.
Yeah. Some frameworks require explicit stuff on say the input box, like form dash control would
be bootstrap and things like that. Right. There's probably some way to set a class.
Yeah. I think there's a class name argument. I'm trying to, I may have,
may have misremembered that. I think it was called class name.
Yeah, sure. Nice. Yeah. I haven't used this really, but I've used it kind of like it in
framework, other frameworks. In the end, I ended up just going back to writing my own
HTML because I felt like I was getting 60% help, but maybe 30% struggle to fight the system. You
know what I mean? And then like, all right, you know what? I just know this stuff well enough.
I'm just going to do it myself. And, and, but if you don't want to mess with it, you want to some
sort of quick development type of thing, or maybe you're not super familiar with HTML. This will
definitely help. Right. Absolutely. Yeah. I think you can also write your own HTML instead and just let
it do the Python validation part. All right. Long as it lines up. Yeah, sure.
Yeah. Yeah. I think it's just the name that matters. Like your input has to have the right name.
Yeah. Because otherwise how would it know, right? It couldn't, it couldn't know.
Yeah, exactly. The CSRF is quite easy and nice. That just puts a hidden input in with the token in,
and it's all taken care of for you. So that can be quite nice.
Yeah. Excellent. All right. Schema.
So schema.
For APIs, right?
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,
but yeah. So we, as an industry, we seem to have moved away from the forms of old and form data to
send in JSON all the time. Right. And a court schema is about saying,
I expect this structure to arrive, validate it for me. So it's a very similar,
it's very similar to FastAPI, which I'm sure a lot of people know.
Okay. That's pretty interesting. So like you say, very much like FastAPI, it kind of brings in
some frameworks called model binding, where you can say there's an argument and it's of this type,
and then, you know, like a class, pedantic class or something along those lines. And it'll,
here's a data class and it'll take all the inputs, right? Query strings or route primers,
whatever, and parse them and map them in there. And then just give you an object and go here,
it's ready. Yeah. So for this library, it supports, I've used all the data documentation as data class,
but it's pedantic based at the moment. And because of the, it's not actually the typing. So there's no
dependency injection or anything like that with FastAPI, but it uses the decorator validate request,
and you pass it the model. And if the request that arrives can be turned into that structure and
validated as such, then your function will be called and that'd be passed in as data. If it can't,
then the client will get a 400 response.
Bad request, bad data.
Exactly. Yeah.
Okay. What's the, so validate request makes sense to me. It means like, here's your structured object
that comes in, passed in as data. What's validate response?
So it's the same concept, but going back. So if you return like a to-do class instance itself,
then there's no validation to do, right? You've already said it's the right structure,
but often you'd return a dictionary or something like that, right? And expect it to go back as
JSON. What this will do is it will say, I'll check that dictionary is of the right structure. And if it
isn't, it throws a 500 response to the client and an error that you can go and view in whatever
error monitoring tool you've got. Sure. Okay. That's excellent. So I see you have data classes
here and you said it also works with Pydantic. Yeah. So, well, actually a shout out to one of
your previous recent streams is I'm working on msgspec support at the moment, because I
actually quite like that. So msgspec is interesting. So it will support Pydantic and message
spec. So you'll be able to use Pydantic data classes, atas, msgspec stuff, or the message
spec struct. I think that's probably all of it, but yeah, quite a bit of choice.
The struct classes really, they've done some super interesting things here to make these
data classes almost better for performance than even regular and for memory and stuff
than regular Python classes. It's quite impressive. Yes, absolutely. Yeah.
Yeah. Just for people who don't know, maybe just tell them real quick what msgspec is.
They probably know data classes and Pydantic. Yeah. So much like data classes, and you can see here,
it's a nice, simple way of creating a class with various things created for you. But it also,
much like Pydantic, will do validation when you try and, if you use one of its functions to convert
raw data like a dictionary into this type. And much like Pydantic as well, it also gives the ability
to take this model and turn it into a open API JSON schema definition. And that's the final thing
Quartz schema does is once you've decorated all your routes, it will auto-generate a open API definition
for you as well. Oh, it will. Okay. Yeah. Using Swaggers/docs. I think that's really nice. A lot of APIs
just don't. They're like, we're not going to do that. Yeah. If they don't have something automated to do it.
And having typed classes representing all your inbound and outbound data, pretty easy, right?
Yeah. I mean, it really helps with myPy, right? Because instead of having like your request.data or
something like that, which is some dict that as far as myPy concerns can be anything, it must be a to-do
instance in this function now. So yeah, really helps with your type checking.
Yeah, absolutely. All right. What do we got next? Sock from Miguel Grimberg. Flask WebSockets.
This one, there is an extension for because this is one of the areas that Quart being natively
async can just do itself. So the Quart, I think I described sometimes as a superset of Flask.
Because it can do everything Flask can do.
The C++ of Flask.
Yeah, exactly.
Awesome. So WebSockets, a problem that websites generally have not been able to do. Like once
you make a request, usually there's the response and that's it. But what if the server, you want to
send more stuff to the server, everyone wants to send stuff out to you later. Like I started this job,
three seconds later, now it's done. Or this bidirectional exchange is really cool. But a lot
of times I feel like it's a little over the top. Like a lot of times I think people maybe just want push
notifications from the server and not true. Like let's have a conversation. I just need to know
when this happens. Just let me know as soon as it's done. Right. So for that, yeah, go ahead.
Server sent events, sorry, seem to be not that well known, but yeah, probably described your usage
really well. That's exactly what I was getting at. So does Quart support SSE? Does it support server sent
events? Yeah, yeah. I think there's an extension for it, but in reality, it's a very small bit of code
you need. So it's yeah. I mean, cause you, you literally return a response with a certain header
and then you just return a, well, here we go. Here's the, there's an example in the documentation
for Quart, but you just return a certain data structure and then you, you've got it. Yeah.
Yeah. So I think that's when people say they want web sockets, I think most of the time,
this is what they want. They want the client, the server to call the client sometimes, not just the
other way around. Yeah. So I'm going to go slightly off topic because it's interesting,
but it's related to this, but one of the things I implemented in Hypercorn, because I find it
really interesting is that I think two, no, it must be more like four years ago, they introduced web
sockets for HTTP two. And of course, one of the reasons you want web sockets because the cost of
opening the connection for HTTP one is, is fairly expensive. So you can keep it open and do the bi-directional.
But with HTTP two, you don't even need an extra connection anymore. You can have your web socket stream on the
same connection as all your requests as well. So it's really nice and efficient. So yeah,
I'd love to have an opportunity to make more use of it. So I find it really interesting, but
the code I write professionally at the moment doesn't quite need that level of real time interaction.
I know, I know all the stuff I built, I'm like, that would be so fun, but you know,
I just don't need that. Yeah, exactly.
I have no use case. I mean, there are apps I'm sure that do it, right? Like really complicated
single page apps like StreamYard that we're using right now. For example, this has probably got some
crazy interchange going on, but I don't write this kind of apps either. All right, let's see. So
the socket stuff, which is cool. That's like you said, built into court. That's one of the advantages
of it. But you know, you can use Miguel Grimberg's stuff to add it to Flask. SQLAlchemy, you mentioned
them before, probably outside. If you're not in the Django space, it's not the only option for sure,
but it's probably the de facto choice people when they think, I want to know our ORM for a relational
database. You probably start here, right? Yeah, I think you would. Yeah. And this is a port,
as I understand it, of the Flask, SQLAlchemy extension to work with or natively work with court. So
should match all your expectations, I think. And if you're used to it, you just use this and it works
with court and there you go. Let's see an example here. You can set up your database with a pretty
interesting nested type of construct here, like just, you know, passing rich arguments that take
more. And if you look at the code on there, just the shape of the code is kind of unique here, but
you end up with this constructed database thing and then it has the base model class and off you go,
right? Just get a session from it, do all the things. I've always had mixed feelings about
these things that you set up here that are built like into the web frameworks,
essentially that make them part of the request. Because if you're going to go and write little
scripts that also do data stuff, you want to want to have, be able to get access to the data and the
models over there as well. All those looks like it does take the app there, but I don't know if it
really needs a meaningful app. Like, I don't think that it would use it probably, but it just adds it.
No, I don't think you need to be within an app context to use the DB. I may be wrong there,
but yeah, I think you could probably use it in a script. Yeah.
Yeah. That's the only thing that concerns me about these like database helpers is like,
you know, I need to do database stuff outside of a request as well. Even potentially some of the stuff
that we're going to talk about in a minute, like it's just a background task that's not part of an
actual request, but it could even still be in the web server process, right?
I use, there's an extension called court DB, which is my preference because I prefer to write the SQL
and go down the OIM route. And yeah, that works as you'd expect. You'd still set it up and pass it
the app, which I think is true of this, but then you can just use it without an app context. It just
basically uses the app to get the configuration values. So it knows where the database is.
Interesting. And now we're on to probably the biggest one that you want to talk out,
talk about, which is quite exciting, court tasks. Yeah. So this is a very recent release that I
haven't announced anywhere else yet. This is something I think I read a post recently that said,
of free web framework, you need like five things. Like you need a database connection. You need some
kind of validation, input, output, rate limiting, authentication, and you need array to run periodic tasks.
And in the past, my way to do this was extra infrastructure. So it was cron running somewhere,
or some kind of, I think cloud watch does some kind of event generator based on a cron schedule.
And for me, that's just annoying because now I have to build extra infrastructure,
which I don't need. I'm not at that scale. Yeah. Maybe you got celery or rabbit MQ or something
like that, right? Yeah, exactly. But again, with those, you need that extra infrastructure for it to work.
Yeah. It's another server. It's another thing that could get hacked. You got to patch it.
You got to secure it. You got to know how to run it. It could go down and then
none of your background stuff works anymore. It's all, it's all a hassle, even though it has a benefit.
Yeah. So what I really want to do is make use of the, the async event loop and just run a task in
the background periodically. And obviously at some point in this one, normally on scale,
because I have so many background tasks that dominate the server and stop it doing requests.
But while I'm small and starting out, this is just makes life so much easier.
Yeah, absolutely. It totally does. And because everything's async and await, you can just set up
the task and just await the task. And it just blends in hopefully well with all the web requests, right?
Yeah. Yeah. So for court tasks, if you write your task as an 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. So if you have synchronous code that's going
to block the event loop, like you're using traditional IO to want for one of the better phrase, then if you just
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.
Yeah. And as long as your async pieces are fine grained enough, you probably won't know.
Yeah. Yeah. It's just if you run a whole bunch of synchronous code or something like that, you might
start to clog things up, but don't do that. Honestly, like what you find, what I find anyway on the web
most of the time is you're talking to other things, talking to a database, talking to a cache, talking to
an API, and all those things are like perfectly suited for await because it's most of the time you're
almost outside the web app. Yeah. I think the perfect one for this that I keep thinking of is
when you want to send a periodic email, like a reminder or a summary of the day, just write this
decorated task run and you have your function that just sends the emails. It's yeah, that's why it
exists for me. It makes that so much easier. Yeah, absolutely. Right. Yeah. Like you say,
just send out a quick email like, "Hey, I need to reset my password." All right, here, we're going to send
you a call on API, like SendGrid or something like that. We're going to send you, but you don't want to block
block things up. You just kick that onto the background work and off it goes.
So one of the things that people would say, all right, you guys, I know you're excited about
async, but we need durability. What about, what if the server goes down or what if another potential
issue is what if you scale, like at least the cron stuff, like what if you ban out your web app into
like 10 worker processes and you say, "Run in five seconds," and they all run in five seconds,
10 times or, you know, those kinds of issues here. So.
So this extension supports a couple of extensions to it. So you can pass it a custom store, which allows
you to store when the task last ran. And so that means if the server goes down and it misses an invocation,
then when it comes back up, it will recognize that and run the task straight away, rather than wait till the next time.
And the other thing it supports is you can decide what should happen before the task starts and what should happen after.
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
where you store this information, where you store the lock, where do you store the. So usually a database works for that.
So yeah, so you can add it all, but you've got to choose how yourself.
That's cool that it has placeholders and ways to plug that in.
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.
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.
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.
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
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.
And say, these are your tasks for the day or something like that.
Sure. There are people who've signed up for the daily digest, send them their digest. Right.
Yeah.
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.
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.
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.
We briefly mentioned court DB, which is one I maintain. So I can say a bit more about that if you're so interested.
Yeah, sure. And it's hanging out right here on your list.
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.
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.
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.
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.
There is one. Yeah. Yeah. Another one that's cool is, minifying all of your elements, right? CSS JavaScript.
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.
Sounds like it does. Yeah. It talks about doing it as a response, right? So yeah. Oh, it does have a cache though.
Ah, okay. Yeah. Well, you get similar. Yeah. Very similar to what I'm suggesting then.
Excellent. Now those are really, really handy. If people go and they want to make their website better, check out, I guess, page speed.
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?
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.
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?
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.
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.
I don't tend to actually write it that much come to think of it, but yeah, it's just makes it easier.
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.
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.
So thanks for being on the show and telling everyone about it.
Oh, thank you. Thank you. Yeah. Great. Hopefully opportunity to introduce some stuff that I hope people will find useful.
Yeah. And just more broadly, like thanks for working on Quart and pushing that forward.
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.
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.
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.
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.
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.
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?
That's effectively what Quart is. I mean, I could, if I called it async Flask at the start.
A Flask. Yeah. It ranks higher in the alphabetical sortings.
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?
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.
Yeah. The selling point is basically if you know Flask, you know Quart, you just use the word async some of the time.
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.
So there's WebSocket.headers and you can do WebSocket.send, send a message, that kind of stuff. So hopefully it just feels familiar.
Yeah. Fantastic. I'm sure it does. And thanks for being on the show and nice to have you back.
Thank you.
Catch you next time.
Yeah, you bet.
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.
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.
Streamlit, Dash, Shiny, Bokeh, FastAPI, Flask, Quarto, Reports, Dashboards and APIs.
Posit Connect supports all of them. Try Posit Connect for free by going to talkpython.fm/posit, P-O-S-I-T.
Want to level up your Python? We have one of the largest catalogs of Python video courses over at Talk Python.
Our content ranges from true beginners to deeply advanced topics like memory and async.
And best of all, there's not a subscription in sight. Check it out for yourself at training.talkpython.fm.
Be sure to subscribe to the show, open your favorite podcast app and search for Python.
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.
We're live streaming most of our recordings these days.
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.
This is your host, Michael Kennedy. Thanks so much for listening. I really appreciate it.
Now get out there and write some Python code.
Thank you.