Learn Python with Talk Python's 270 hours of courses

Quart: Flask, but 3x faster

Episode #147, published Fri, Jan 19, 2018, recorded Thu, Jan 18, 2018

Guests and sponsors
There has been a bunch of new Python web frameworks coming out in the past few years. Generally, these have been focused solely on Python 3 and have tried to leverage Python's new async and await features.

However, generally these frameworks have come with their own new APIs. They may be amazing but it's something new to learn and a barrier to migrating over to them.

That's why when I learned about Quart from Philip Jones, I was excited. It's an async-enabled web framework that attempts to be 100% compatible with Flask, including the extensions.
Quart: gitlab.com/pgjones/quart
3x faster Flask apps: hackernoon.com
Phil's PyCon UK Talk: youtube.com/watch?v=EgpQcLy1kf0
Reddit Announcement: reddit.com
PeeWee ORM Async: github.com/05bit/peewee-async
Controlling Python Async Creep article: hackernoon.com

Sponsored links
Smarkets careers: smarkets.com/careers
Rollbar error monitoring: rollbar.com
Talk Python courses: training.talkpython.fm
Episode transcripts: talkpython.fm

--- Stay in touch with us ---
Subscribe to Talk Python on YouTube: youtube.com
Talk Python on Bluesky: @talkpython.fm at bsky.app
Talk Python on Mastodon: talkpython
Michael on Bluesky: @mkennedy.codes at bsky.app
Michael on Mastodon: mkennedy

Episode Transcript

Collapse transcript

00:00 There have been a bunch of new Python web frameworks coming out in the past few years.

00:03 Generally, these have been focused solely on Python 3 and have tried to leverage Python's

00:08 new async and await features.

00:09 However, these frameworks have come with their own new APIs.

00:13 They may be amazing, but it's still something new to learn and a barrier to migrating over

00:18 to them and between them.

00:20 That's why when I learned about Cort from Philip Jones, I was excited.

00:24 It's an async-enabled web framework that attempts to be 100% compatible with Flask,

00:29 including the extensions.

00:30 This is Talk Python to Me, episode 147, recorded January 18th, 2018.

00:37 Welcome to Talk Python to Me, a weekly podcast on Python, the language, the libraries, the

00:55 ecosystem, and the personalities.

00:57 This is your host, Michael Kennedy.

00:59 Follow me on Twitter, where I'm @mkennedy.

01:01 Keep up with the show and listen to past episodes at talkpython.fm.

01:05 And follow the show on Twitter via at Talk Python.

01:08 This episode is brought to you by SmartKets and Rollbar.

01:11 Be sure to check out what they're offering during their segments.

01:13 It really helps support the show.

01:15 It's that conference time of year, everyone.

01:18 There's actually a bunch of interesting things happening around Python conferences.

01:22 So let's do a quick update.

01:24 First, we'll do this chronologically.

01:26 First, Pi Cascades is happening in Vancouver, BC, January 22nd and 23rd.

01:31 I'm going to be there.

01:32 So if you are one of the lucky people to have actually gotten a ticket before they sold out,

01:36 hope to see you there in Vancouver.

01:40 Next, we have Pi Colombia in Medellin, Colombia.

01:42 This is February 9th, 10th, and 11th.

01:45 So if you're in South America or want to go to South America and would love to go to this conference,

01:49 please check out the Pi Colombia conference.

01:52 Those guys are doing cool stuff down there.

01:53 Next up, PiCon Slovakia.

01:56 This is March 9th to 11th in Bratislava.

01:59 And I'm actually going to be speaking there and doing a workshop.

02:03 If you're in Europe and you can make it to Bratislava in March, that would be awesome.

02:07 Please come say hello or attend one of my talk show workshops.

02:10 And finally, the big one, PiCon US in Cleveland, Ohio, May 10th.

02:15 I personally just finalized all my travel plans.

02:17 I hope to see you there.

02:18 There's still tickets available.

02:20 They're not yet sold out as far as I know.

02:22 So hurry, hurry, because just like Vancouver, they will sell out.

02:26 We're also going to have a booth with lots of cool giveaway stuff there.

02:28 So please stop by our booth and say hello if you make it to PiCon US.

02:32 All of these conferences are amazing.

02:34 And I hope you can make at least one of them.

02:36 Now let's get to the interview.

02:37 Phil, welcome to Talk Python.

02:39 Thank you.

02:40 Hello.

02:40 It's great to have you here.

02:42 I'm a big fan of asynchronous programming.

02:44 And I consider myself really a web developer at heart.

02:48 And so this project that you are creating is really, really interesting to me.

02:54 Port, kind of an asynchronous version of Flask.

02:57 And we're going to get into all the details in that and really dig into it.

03:00 But before we do, let's start with your story.

03:02 How did you get into programming in Python?

03:03 So I got into programming to really to make games when I was a teenager.

03:08 The first one was specifically was to try and make my own version of Cannon Fodder, which

03:13 I quite enjoy playing around my friend's house.

03:15 So that's how I got into it.

03:16 And it was VB originally for me.

03:18 VB, like VB6 type of thing?

03:20 Yeah, I think it must have been.

03:22 Yeah.

03:22 Or like the Microsoft Visual Basic, right?

03:24 Yeah.

03:25 Oh, yeah.

03:26 Very interesting.

03:27 That's a long ways from the web.

03:28 Yeah, definitely.

03:29 It is cool.

03:31 So you started out in Visual Basic and created that.

03:34 And that sounds really fun.

03:35 Where'd you go from there?

03:36 Originally figured of doing computer science at university, but I did physics instead.

03:40 But over the time, I got more and more into coding.

03:43 And by the time I did my PhD, that was quite heavy on the simulation parts.

03:47 And later on in the postdoc, I switched, roughly speaking, from C++ to Python.

03:53 So I'm reasonably new to Python.

03:55 But that's the kind of overview of how I progressed in the languages.

03:59 Oh, yeah.

04:00 Really cool.

04:00 What was your PhD focus?

04:02 It was background rejection in neutrino experiments in Canada.

04:05 And the simulation of the experiment itself.

04:09 So a lot of physics simulation.

04:10 Oh, yeah.

04:10 That's cool.

04:11 And of course, computation is really front and center in that kind of stuff, right?

04:15 You can't really explain it anymore without the Monte Carlo simulation.

04:19 It's too complex.

04:20 Yeah.

04:20 Monte Carlo simulations are really amazing.

04:23 They seem like magic to me.

04:24 Here's the thing that'll take a month.

04:25 Oh, but we can do it in five minutes.

04:27 Very cool.

04:28 If you're willing to accept a little uncertainty, we can do it really quick.

04:30 All right.

04:31 That's cool.

04:31 So what do you do day to day?

04:33 Not still physics simulations, right?

04:35 No.

04:35 I left physics about three years ago now.

04:38 I work for a company called Smartkits in London.

04:40 And they're a betting exchange or event trading exchange.

04:44 So ideally, we become like the prediction market.

04:47 So you go to figure out who's going to win an election, for example.

04:51 That's what we want to be.

04:52 Oh, that's really cool.

04:53 So probably a lot of website traffic.

04:55 Do you have APIs and stuff people can consume?

04:58 Exactly.

04:59 So we build the website and the API to interact with the exchange to allow people to trade.

05:03 So yeah, it's quite heavy on Python.

05:06 There's some JavaScript and some Erlang as well.

05:08 Erlang.

05:08 Okay.

05:08 Very cool.

05:09 That sounds like a fun thing to do day to day.

05:11 And it's not that far removed from this project we're going to talk about.

05:14 So I kind of want to start the conversation and set the stage by just talking about

05:21 asyncio in general.

05:23 So this was introduced in what?

05:26 Python 3.4, right?

05:27 I think so.

05:28 Yeah.

05:28 I think it was the Tulip project before then, which you could use with Python 3, I think.

05:32 So yeah, I think it came in the standard library in 3.4.

05:36 All right.

05:36 So what's the main idea behind Async IO?

05:38 I think it's about trying to utilize the CPU as much as you can.

05:43 So instead of just being idle while you're waiting for IO, you've switched to something

05:47 else.

05:47 You gain the concurrency that way.

05:49 To me, that's what it's about.

05:51 Yeah.

05:51 I think one of the real interesting things is web requests are so often waiting on other

05:58 things.

05:59 Like at the web server level, at the web framework level, a request comes in and it says, hey,

06:05 I'm this user.

06:06 I care about this data.

06:07 What's the very next thing you do?

06:09 You either call a microservice.

06:11 You maybe find it on disk or you go talk to a database.

06:14 Regardless, that whole process is just going, and I'll just be here for when you need me

06:20 waiting for you to get back to me on that data so I can give it to the user, right?

06:24 Yeah, exactly.

06:24 And so if you could somehow say, well, let's pause that until that bit of work has an answer

06:31 back from the Async IO, from the IO conversation, right, to the database or whatever, and let some

06:40 other part of code that's going to run, run, so it can then begin to wait, you know, a lot

06:45 of hurry up and wait on the web server.

06:46 And so the Async IO basically means if you're waiting on IO, that same thread can be processing,

06:52 can release the GIL and be processing other things, which I think is especially important

06:57 in the web world.

06:58 I think it goes a bit further as well.

07:00 Like if you compare it to say threading, which is another way to achieve the same thing in that

07:06 it's obviously a coroutines are much more lightweight.

07:09 So you can handle many more of these requests at any point in time than you can with threads.

07:15 So that's the other part of it that makes it really useful for web servers.

07:19 Yeah.

07:20 The lightweight part is really, really interesting.

07:22 I mean, anyone who's sort of worked with a generator, it's kind of, kind of like that.

07:27 And threads themselves have all sorts of overhead that comes with them, creating a thread, destroying

07:32 a thread.

07:32 So maybe you put them into a thread pool, but threads themselves, they have context switches

07:37 at the kernel CPU level.

07:39 They may mess up the cache, right?

07:42 The L1, L2 cache.

07:44 And so then they kind of can wreck performance as you cycle between the threads.

07:48 You know, the cost of a thread varies by operating system, but they can have like a meg of stack

07:55 space allocated to them.

07:56 There's all sorts of things that limit how many you can have.

07:59 So you can have a 10, no problem.

08:01 A hundred, no problem.

08:02 A thousand starts to, you know, maybe push the actual memory limits of your computer.

08:06 With these, the sort of asyncio stuff, you can have many thousands, which is, I think, really

08:12 awesome.

08:12 So maybe give us a quick comparison to this concept with like Eventlet or GeoVent.

08:18 How are they similar or different?

08:19 So I think Eventlet and GeoVent are the ones you typically use with Flask at the moment.

08:25 And I think Eventlet is roughly started out as a fork of GeoVent.

08:31 So they're similar in principle, those two.

08:33 And they're all three, including AsyncIO, are very similar in principle.

08:38 They're an event loop that runs tasks or greenlets or coroutines and allow yielding when there's

08:45 IO.

08:45 But I think the crucial difference and what AsyncIO, I think, does differently to all the

08:51 choices I've seen so far is that it makes the yielding explicit.

08:54 You have to write the await keyword.

08:57 And that fits the Python philosophy a lot better.

09:01 Obviously, explicit being better than implicit.

09:04 And I think it makes it a lot clearer to the user because you now know when these changes

09:10 are going to happen.

09:10 And you get a feeling that you're actually writing these yields in your code rather than

09:15 there's some magic in the background making it happen.

09:17 Right.

09:17 Exactly.

09:18 That's very cool.

09:19 So basically, anytime you're going to call one of these Async methods, you say await.

09:24 And that signals to the runtime to say, now we're going to give up this execution and pause

09:31 until this IO or this event completes.

09:34 Go on.

09:35 And you sort of really clearly know and call out where things are going to sort of pause,

09:41 at least for that request, but also yield the execution.

09:44 Right.

09:44 I think it was probably a little clearer before they introduced the Async and await keywords

09:49 because you'd write an explicit yield to actually yield and a yield front to just switch control

09:55 to your next coroutine.

09:56 Whereas just with the await keywords, you could await a coroutine or await something that does

10:01 yield, but it's not quite clear as much.

10:04 But still, you know that that line could possibly yield, which makes it a lot clearer.

10:09 Yeah.

10:10 Yeah.

10:10 That's really cool.

10:11 So one thing that you've talked about is this AsyncIO color problem.

10:15 What's that?

10:15 I can't remember who termed it as such.

10:18 Yeah.

10:19 I certainly copied their choice because you could imagine coloring certain colors red and the

10:25 red functions call other red functions and other functions black and the same applies.

10:30 And the red functions would be coroutines asynchronous functions and the black ones just use synchronous

10:35 ones.

10:35 So I think that's where the naming comes from.

10:38 But the basic problem part of it is that the explicit design forces you to only be able

10:44 to trigger coroutines asynchronous code from coroutines.

10:48 So if you have a synchronous function, your standard Python function, it's quite hard to run a coroutine.

10:54 Right.

10:54 And that's one of the hearts of the problems of why so many popular web frameworks don't

11:01 just enable async.

11:03 Right.

11:04 Because it comes in through this WSGI interface.

11:06 It calls, you know, the one function that is in that API and it starts out synchronous.

11:12 And so there's really no layer, no place where you can sort of inject that easily.

11:18 Right.

11:19 Not only is it viral in nature, as soon as you want to await something, you need to be in a

11:24 asynchronous in a coroutine to do so.

11:26 To also call that coroutine, you need to be in a coroutine again.

11:30 So you go all the way up to the event loop that inevitably runs your coroutines.

11:34 And WSGI, at least in its current form, doesn't have a concept of event loop.

11:39 So it's not having that.

11:41 It's also not going to do the IO necessarily in a way that's going to yield to any event loop

11:47 for you.

11:47 So even if you introduce it later on, you're not necessarily getting the full advantage

11:52 of yielding on the IO.

11:54 You basically have to block and wait to give the request back at some point, or you're going

11:59 to get ahead of yourself.

12:00 And that pretty much cancels out all the benefits that you had.

12:03 So your project says, we're going to take this and we're going to start from this asynchronous

12:09 level.

12:10 One thing I do want to call out, and I can't quickly look up where it is, but there's this

12:16 great article by Christian Medina that says, it's titled something like Controlling Async

12:22 Creep.

12:23 And he talks about this problem, this asyncio coloring problem, and different techniques

12:29 you can have to sort of create boundaries or whatnot.

12:32 Very, very interesting article around this idea.

12:34 But let's stay focused on this.

12:36 So maybe before we get into how people work with Flask, I feel like the Flask API is showing

12:44 up everywhere.

12:45 For example, I just recently did a show on Flask Ask, which is like a Flask API for writing,

12:51 say, Echo Dot type voice interactions.

12:55 And you just write it as a Flask app, and it just magically plugs into that whole framework

13:02 from Amazon, which is really cool.

13:03 So why do you think Flask is so popular?

13:05 That does sound really cool.

13:07 I think it's a very clear and concise API to use.

13:15 I think if you're a very simple example that hello world with the decorator saying this

13:21 is the root and this is the view function, I think you couldn't get an API clearer than

13:25 that for a web framework.

13:27 It's so concise and nice.

13:29 And I think the design choices Flask has made as a whole just really kind of emphasize this

13:35 usability.

13:36 So it might be the request object being a global, for example, just because you're going

13:42 to use it that way, it makes life so much easier.

13:44 And so I think this and the familiarity of that API is what kicked it off.

13:50 And then I think that enabled the really strong community around Flask that probably makes it

13:56 really popular today.

13:57 So the great blog posts you can find, especially effectively the large blog post I think everyone

14:04 starts with from, I think, Michael Grinberg and the extensions on Miguel.

14:10 Yeah, Miguel Grinberg.

14:11 And he just, he's actually redoing that one for Python 3 and modern Flask and all that.

14:16 So like he's halfway through redoing that, which is really exciting as well.

14:20 Yeah, I'll be sure to link to that.

14:21 I agree.

14:22 I think there's also so many plugins you can get to sort of extend Flask, right?

14:26 There's extensions for almost anything you could want to do.

14:29 Let's talk about this for a second.

14:30 What if I have a Flask app, standard Flask, not Quart or anything, frequency like that, and

14:36 I want to do something asynchronous in it?

14:38 Like, let's say I want to do WebSockets, which are basically permanent connections, right?

14:43 That can't easily be done synchronously in sort of a request response style.

14:48 So how would that go?

14:49 Can you do it?

14:50 Yeah.

14:51 I think typically at the moment you'd use Gevent, at least the two popular ones, which

14:55 is Flask Sockets, the popular extensions, and Flask Socket.io, I believe use Gevent under

15:00 the HUD.

15:01 And you do need the event loop really to make it possible.

15:05 And with those, it's actually quite easy.

15:08 I think you just add that extension.

15:11 It takes care of the rest for you.

15:12 And you can just decorate a function that you say is a WebSocket root and deal with the WebSocket

15:19 directly.

15:19 It's very easy.

15:20 I see.

15:21 So does it basically like hand it off to that processing to just run asynchronously on its

15:27 own?

15:27 Well, if we take the Flask Socket example, what that does is it'll wrap your Flask app

15:32 in a Gevent whiskey server and introduce the WebSocket at that level.

15:37 So a WebSocket request coming in would be handled before it gets to Flask, although it looks like

15:43 it's going to Flask, but it would be handled beforehand.

15:45 And then everything else would just go straight through to Flask with the whiskey interface.

15:49 So yeah, it would work that way.

15:52 Yeah.

15:52 Nice.

15:52 This portion of Talk Python is brought to you by SmartKits.

15:56 And they're looking to hire someone to write some really awesome Python code.

16:00 SmartKits operates a world-leading exchange for peer-to-peer trading on sports, politics,

16:05 and current affairs.

16:05 As a business, SmartKits is widely recognized as one of the fastest-growing tech companies

16:10 in Europe and has won a roster of awards for its success over the past few years.

16:14 Headquartered in London with a new tech hub in downtown LA, the company is pioneering a

16:20 self-managed organizational structure.

16:22 This breaks down the traditional pyramid of hierarchical silos into a more fluid and

16:26 flat network of interlinked teams who are engineers of the driving force.

16:30 They're no formal bosses.

16:31 Staff are allowed to set their own salary, have unlimited holiday, and work where they like

16:36 within the company.

16:37 Over 60% of the staff are engineers who work on a modern tech stack predominantly based on

16:43 Python, complemented with Erlang and JavaScript.

16:45 SmartKits uses Python 3.6 throughout and deploys dockerized microservices multiple times a day.

16:52 Apply to work at SmartKits by visiting talkpython.fm/SmartKits or just click the link in

16:58 the episode notes.

16:59 Suppose I want to have some Flask view method and it's going to say call a web service.

17:07 It's going to talk to a Postgres database, for example.

17:10 There are asynchronous ways to do those things, right?

17:15 If I have AIO HTTP client, that's a really nice AsyncIO friendly way to call remote services.

17:22 And I could use the Async Postgres driver there and call that.

17:28 But with straight Flask, I can't put like Async on my methods, right?

17:33 Or await in it and really get it to honor that, could I?

17:36 I don't think so.

17:37 There was an extension called Flask aiohttp, which I think is no longer maintained.

17:43 But that, I believe, used the WSGI interface that aiohttp provided, which I think is also

17:49 deprecated now, sadly.

17:50 But that would allow you to fairly easily, I think, make your Flask view functions async.

17:56 But it wasn't, I think it had some issues whereby the locals, the requests, gee, those request

18:04 locals would get corrupted or could get corrupted.

18:07 So I think that kind of ruled it out.

18:09 Yeah, that doesn't sound amazing, right?

18:12 No, no, you certainly don't want that.

18:13 There was also a fork, a Flask I found that did make the view functions async.

18:20 It was designed around async AO for 3.4.

18:24 But I haven't quite tried to figure out how it worked or if it worked, but it hasn't been

18:29 touched for about three years.

18:31 So I don't think that's maintained either.

18:33 Right.

18:33 And so this maybe brings us to your project, Quart.

18:37 All right.

18:37 What is Quart?

18:38 So Quart is a web micro framework, much like Flask, based on async AO and the Flask API.

18:44 So the aim really is to provide the easiest stepping stone from someone who has a Flask

18:50 app or Flask knowledge to use async in that app or in a new app with that knowledge, basically.

18:56 So yeah, it's just a web framework, web micro framework.

19:00 Nice.

19:00 So you decided instead of, because there are other asyncio frameworks that have come

19:06 around, Sanic, Chepronto, others that we can talk about later.

19:09 But generally they said, we're going to come up with a new way to program the web and we're

19:15 going to sort of do it around this asyncio thing.

19:17 And you said, look, Flask is super popular.

19:20 It has this API that people already know.

19:23 People can go and take Miguel's tutorial and learn about it already.

19:28 But you just want to have it work with asyncio, right?

19:33 And so we're going to start from there, right?

19:35 How much did you borrow from Flask and how much did you have to start from scratch here?

19:39 I've tried to borrow the entire Flask API and quite a bit of the Verkser API, which is

19:45 the part under Flask that powers most of the HTTP stuff.

19:49 I've tried to borrow most of that as well.

19:51 So yeah, a great deal, hopefully.

19:53 What I'd really quite like is if you have a Flask app that doesn't use any extensions, you

19:59 can just find, replace Flask with Quart and then add the async and await keywords and it

20:03 just runs.

20:04 That's what I'm kind of aiming for.

20:05 That's great.

20:06 And how close is it to that goal?

20:08 I think it's quite close.

20:09 I think it's really the details now.

20:12 So for example, I need to work on the e-tag handling, how they're applied to static files

20:18 and stuff like that.

20:19 And subdomain handling in the routing system.

20:22 I think those are real details that I think most of the use cases, it should be possible

20:27 to just do that now.

20:27 To just, as in, find, replace.

20:30 Oh, that's awesome.

20:31 You could go like grab Miguel's tutorial and just, I don't know, what's the verb of making

20:35 it run on Quart?

20:36 Quartify?

20:37 Add Quart capabilities to it, right?

20:39 That'd be cool.

20:40 Very cool.

20:41 I guess one of the first questions is why not just fork Flask and just tack on the little

20:47 bits of asyncio handling that you need?

20:50 That would be ideal.

20:51 Well, but at the moment, at least, it's beyond what I'm able to do.

20:56 And I think it goes back to the Whiskey interface, which really isn't asynchronous.

21:01 So, again, like if you really want the event loop to be able to get the yield on the request

21:06 IO and the response IO, you need to be controlling that part.

21:09 And so I think you have to go start from Flask, go up to VerkSug, and then almost go up to the

21:17 WSGI servers themselves and make them asynchronous to really, well, I think IO compatible to really

21:23 make it possible.

21:24 And, yeah, I could really make that work.

21:28 It was quite hard.

21:30 I know a lot of people have tried and they've talked about adding like a Whiskey 2 or various

21:39 acronyms with A involving in there to basically asyncify that API.

21:44 But there's really not a lot of flexibility in there.

21:47 And the actual Whiskey API, which is what all the web frameworks use to plug into the

21:53 various web servers, right?

21:54 I want to run on MicroWSGI.

21:56 I want to run on Genicorn or whatever.

21:58 They all speak Whiskey.

21:59 So you can just plug Pyramid, Flask, Django, whatever in there, right?

22:03 One of them I was looking at is ASCII, which I think Django is pushing.

22:07 So I think the idea there is you just push out the messages to a queue and then you have loads

22:13 of things consuming it in an asynchronous fashion and then returning the results.

22:18 I don't think it would quite work for Quart either, but they seem to want to.

22:22 I think they're trying to suggest that as the next step for Whiskey.

22:25 Like it becomes ASCII.

22:26 Yeah, yeah.

22:27 That's one of the acronyms I was thinking of.

22:29 So how do you add on things like WebSocket support or HTTP2 pipelining where you can make

22:37 a request and actually through that in single network request punch like three CSS, a JavaScript,

22:43 an image, an HTML file response as one.

22:46 Do you think they could make that work there?

22:48 I think so.

22:49 Yeah, because each request could just be a separate message on their queue and then something can

22:54 consume and produce the output and send it back.

22:57 So I'm sure it would work for them.

22:59 Maybe you could somehow bundle up the multiple responses or something at the framework level

23:04 and then send it over to the network.

23:06 I don't know.

23:07 It's yeah, it's going to it's going to be pretty interesting with all the HTTP2 stuff coming

23:11 along and whatnot.

23:12 Oh, definitely.

23:13 I think HTTP2 is one of the most more exciting things I'm interested in.

23:17 And I'm very pleased that Quart can do it.

23:20 So it's very good to play with.

23:22 It can.

23:22 That's really cool.

23:23 So how much of that is the framework and how much of that is, say, just the server it's

23:27 running on?

23:28 Say if you run on G Unicorn, right?

23:30 How much is G Unicorn doing versus how much of is Quart actually doing to make the HTTP2

23:35 supported?

23:36 In a traditional sense, the WSGI server does all the HTTP passing and just passes through

23:40 the environment.

23:41 But for Quart, how it works with G Unicorn is we just use the socket.

23:46 So G Unicorn doesn't actually do any HTTP passing.

23:50 It all goes through to Quart.

23:51 So all the HTTP2, the HTTP1, it's all taken care of in Quart.

23:55 And we just pipe it back out through the socket that G Unicorn's provided.

23:59 Give us a sense around the kind of performance differences that people could expect.

24:04 If they, say, have a standard Flask app running on G Unicorn now, if they flip to using Quart

24:10 also on G Unicorn, what do you think happens?

24:13 I actually did a, I think you're going to link an article I did to look at this.

24:17 And I looked at a kind of production use case where you have a simple CRUD app with a database

24:24 in the background.

24:25 I used G Unicorn with eventlet.

24:27 So it was asynchronous, fairly similar type of work load pattern and the way it approaches

24:34 the problem.

24:35 And I compared it against Quart.

24:36 And I think with Quart, I got something like a free time throughput speed up.

24:41 So the latency or the request time itself didn't really make too much of a difference.

24:47 But the amount of requests it could handle at once really increased.

24:50 Yeah, that's really cool.

24:51 And I would guess that you're probably were not working with like a super slow database

24:57 or a tremendous amount of data in your simple test.

25:00 But it seems to me like the worse the database performs, the more beneficial having this asynchronous

25:07 thing could be to sort of free up the waiting.

25:11 The waiting, the worse the waiting is, the more beneficial is to have async methods.

25:16 Although that should apply to both sides of the test because eventlet would do the same

25:21 in the flask sense.

25:22 Right.

25:23 OK, yeah.

25:23 So if you're already switching to eventlet, then it would.

25:27 Have you thought about this relative to a non-async flask, like just a standard flask, if you're

25:34 not using eventlet?

25:35 Yeah, it makes a huge difference then because your requests effectively become synchronous.

25:39 You have to do one at a time.

25:41 So unless I suppose you used threading.

25:43 I haven't tried threading, but assuming you've got no asynchronous aspect at all, then it would

25:48 make a very large difference because you would have to finish the one request before you could

25:52 even start the next.

25:52 That's for sure.

25:53 How interesting.

25:54 OK, so you talked about flask and I said one of the benefits of it is these flask extensions,

26:01 right?

26:01 There's a bunch of stuff you can plug in.

26:03 Does Quart support flask extensions?

26:06 Supports a good fraction of them, I think it's fair to say.

26:10 So this goes back to that color problem we mentioned earlier, because, of course, all the

26:15 extensions are synchronous.

26:16 They have synchronous functions.

26:18 And inevitably, they're going to try and call some code that's now asynchronous in Quart.

26:23 And that's where it becomes a problem.

26:25 So I can go in the details of how it gets around that.

26:29 But that's why some of them work.

26:31 Yeah, that'd be kind of interesting, right?

26:33 Because people are going to pip install these extensions and generally not change the code.

26:37 And until your framework is popular enough that people are creating sort of async equivalents

26:44 of their extensions, you have to make these, you have to blend the colors, right?

26:48 So how are you doing that?

26:49 It's quite fun to look at.

26:50 It took quite a bit of time.

26:51 So if I go back to how you run a coroutine, you need to run it in the event loop.

26:56 And if you're outside of the event loop, it's quite easy.

26:58 You just either create or get an event loop and tell it to run a coroutine.

27:02 And it's all very good.

27:03 But if you're in a function that's been called by something within the event loop, you can't

27:08 do that.

27:08 The actual async arrow function run until complete, for example, will refuse to run because you're

27:14 inside the event loop.

27:15 And it turns out this was known back during the tulip days.

27:20 And someone proposed a solution to this, which was to actually run the event loop again within

27:26 the event loop to run this particular coroutine.

27:29 And I think it was rejected because it just made things quite complicated and wasn't a particularly

27:35 useful use case.

27:36 But in this case where you've got legacy, well, you have synchronous code, trying to call

27:41 asynchronous code is exactly what you need to do.

27:44 So what Quart will do if you want to go down this route is monkey patch the event loop to

27:49 add a method to run the event loop, if you like, manually for a coroutine that you specify.

27:56 So it will suspend the event loop it's already in, run it itself, and then restore everything

28:01 after it's complete.

28:02 So it probably sounds a bit messy.

28:04 So it probably is.

28:07 It sounds like it could create this cascading chain of nested event loops going down and

28:12 down and down.

28:13 Yeah, probably so.

28:14 But it does allow like these extensions with a synchronous call to another function to be

28:21 able to call an asynchronous function without them being able to tell.

28:24 There's just a layer in between that does this kind of mapping from sync to async.

28:28 That's really cool.

28:29 I'm very creative.

28:30 So you said it works for most of them.

28:33 When it fails, what happens?

28:34 Like, why does it not work?

28:35 Do you remember?

28:36 So Flask has these request locals, like the request object or the G object.

28:41 And when you try and do anything with them or access any attribute of them, it effectively

28:46 proxies that action to an instance that's local to the thread or greenlit that you're on.

28:53 And I hope that makes sense.

28:54 It's proxying, basically.

28:56 So what I can do in Quart is I can, during that proxy, also take it from a synchronous call to an

29:02 asynchronous call.

29:03 So that works really well for these local proxy objects.

29:07 The problem is when an extension uses the Flask class itself, like the app, because I haven't

29:13 figured out a way yet to effectively proxy the call and convert it from synchronous to asynchronous.

29:18 So if the extension just uses these globals, it's good.

29:23 If it uses the app, I need to be a bit more clever.

29:25 Hopefully you get that puzzle figured out, because it'd be really cool to have that supported

29:29 there.

29:29 Very nice.

29:30 So what template languages are supported?

29:33 You have Jinja 2?

29:35 It follows the Flask design in that respect and just does Jinja 2.

29:38 But I'm sure like the, I think there's an extension called Flask Maco.

29:42 It's quite popular.

29:43 I think that may work as well.

29:46 The question becomes whether that template in engine is asynchronous itself.

29:50 So if it isn't, you just get a bit of a performance hit.

29:53 Right.

29:53 I see.

29:54 So the Jinja 2 one supports asynchronous behaviors in directly?

29:59 It does.

29:59 It's actually, if you look at the code, it's kind of amazing how they do it.

30:03 So if you're running Python 3.6 and you ask it to be asynchronous, it will patch itself to

30:08 add the asynchronous methods.

30:10 It's, yeah, it's really quite good to look at.

30:12 Wow, that's quite awesome, actually.

30:14 I didn't realize it did that.

30:15 So I'm guessing you don't support Python 2 in this.

30:19 Is it Python 3 only?

30:20 It is.

30:21 It's even more restrictive than that, actually.

30:23 It's Python 3.6 only because I use asynchronous generators.

30:27 One of the uses being if you want to stream a response, you want to yield data back whilst

30:32 being asynchronous.

30:33 So because of that, you can't use anything other than Python 3.6 where they were introduced.

30:37 Yeah, that's really interesting.

30:39 So I feel like a few years ago, the story was, well, people can't move to Python 3 because

30:44 there's all this cool stuff that we're using that only supports Python 2.

30:48 And now I think more and more we're ending up in a situation where it's there's all these amazing new things.

30:54 But they're only accessible to you if you're using the latest versions of Python 3, which

30:58 I think that's a good move.

31:00 The other thing it does as well, which really constrains it to Python 3 or I've used is I

31:04 type into everything, including the variables, which the syntax, well, the nicest syntax was

31:09 only introduced in Python 3.6.

31:10 So yeah, I quite like that as well.

31:13 I really do as well.

31:14 I find it makes the editors much smarter about the types of things you're actually working

31:18 with.

31:19 You can run tools and say, no, no, you're passing an answer.

31:21 You're supposed to pass the whole object, not the ID of the object here.

31:24 Things like that.

31:25 It's great.

31:26 This portion of Talk Python to Me has been brought to you by Rollbar.

31:30 One of the frustrating things about being a developer is dealing with errors.

31:33 Relying on users to report errors, digging through log files, trying to debug issues, or getting

31:39 millions of alerts just flooding your inbox and ruining your day.

31:42 With Rollbar's full stack error monitoring, you get the context, insight, and control you need

31:47 to find and fix bugs faster.

31:49 Adding Rollbar to your Python app is as easy as pip install Rollbar.

31:53 You can start tracking production errors and deployments in eight minutes or less.

31:57 Are you considering self-hosting tools for security or compliance reasons?

32:01 Then you should really check out Rollbar's compliant SaaS option.

32:05 Get advanced security features and meet compliance without the hassle of self-hosting, including

32:10 HIPAA, ISO 27001, Privacy Shield, and more.

32:14 They'd love to give you a demo.

32:16 Give Rollbar a try today.

32:17 Go to talkpython.fm/Rollbar and check them out.

32:21 Yeah, so actually converting your Flask methods to Quart methods is quite easy, right?

32:32 You just, you have your, let's say, like a review function.

32:35 I think this is an example you have on your website.

32:36 You can say def add review data equals request.getJSON.

32:40 Or you can just say async def add review data equals await request.getJSON.

32:46 And you just add the async and await keywords and off it goes, right?

32:50 Yeah.

32:50 So I think it wouldn't be that hard to convert a small to medium-sized Flask app to Quart.

32:57 What do you think?

32:57 How much effort and testing has to be done?

32:59 I think you're right.

33:00 I think small ones should be quite easy.

33:03 What probably make it hard for people is that they're probably going to call something else,

33:08 like you were saying earlier.

33:09 If you're going to do a web request to a, or a PHP request to a microservice or something

33:14 to a database or something like that.

33:16 And those libraries are likely to be synchronous rather than async.

33:20 And while this comes back to the color problem, you're going to have to switch from, say,

33:24 psycopg to asyncpg or your py Redis to like aioredis or requests to aiohttp.

33:32 And that's probably going to be more work because those APIs are suddenly different usually.

33:37 I would think probably the Redis wouldn't be that hard.

33:40 Probably the AIO HTTP client, not such a big deal.

33:43 But I feel like when you get down to database stuff, that's where the complexity lives a lot

33:49 of times.

33:49 So maybe upgrading the database driver or package to be the async one is probably where it's

33:56 most challenging.

33:57 I don't know.

33:57 Have you tried?

33:58 Have you got some experience?

33:59 Yeah, I've played along with myself.

34:03 And yeah, it's because I typically use psycopg directly.

34:07 I don't use SQLAlchemy on top of it usually.

34:10 And that's not too bad.

34:12 It's fairly easy to change.

34:13 And it has a big bonus.

34:16 Like there's an asyncpg.

34:17 If you look at how MagicStack report the benchmarking for it looks much, much quicker, which is excellent.

34:24 But yes, if you have a lot of ORM stuff, I think you're almost stuck.

34:28 I'm not sure I've seen an ORM that's async yet.

34:31 I know that SQLAlchemy hasn't done it.

34:35 I feel like there is one out there.

34:39 And I'm hesitant to say which one I'm guessing it is because if it's wrong, I'll try to put

34:46 some notes in the show notes or something.

34:47 I've seen one that I thought allowed it.

34:50 But yeah, certainly SQLAlchemy doesn't.

34:52 And that's unfortunate, right?

34:55 So it kind of says, look, if you're going to do the async stuff, then you're kind of stuck.

35:00 I guess there's a few things you could do.

35:04 You could move your requests out, say like another thread that you wrap up and you await

35:10 that thing's response or something.

35:12 But it definitely makes it not easy, right?

35:15 Yeah.

35:15 So asyncio makes that bit reasonably easy.

35:19 I think there's a function called running executor, which will do almost exactly what you just said.

35:23 So yeah, that's probably what you'd have to do.

35:25 Okay.

35:25 So I found what I was looking for and it's actually not the ORM itself.

35:29 It's an add-on.

35:29 It's called P.

35:31 So the ORM is PeeWee, which is a sort of a small ORM type of thing.

35:36 Right.

35:36 It's, it's pretty popular.

35:37 It's got close to 5,000 stars, but there's PeeWee dash async, which is an asyncio interface

35:44 for PeeWee.

35:45 So that one, that one you can do it.

35:48 So you can basically say await objects.create or things like that, where you basically do

35:55 the queries and then you can await the response, which is kind of cool.

35:58 Oh, that is cool.

35:58 Yeah.

35:58 I didn't come across that.

35:59 Yeah.

36:00 But it's unfortunate that it's not like the most popular one, like SQLAlchemy or say the

36:05 Django one or something like this, right?

36:07 Like it's, yeah.

36:09 But if people are looking for it, like maybe you'd have to switch to PeeWee async if right

36:13 now it's written in say SQLAlchemy.

36:14 I don't know.

36:15 So what do you think?

36:16 I'm thinking about this database problem.

36:17 Like, can you take a SQLAlchemy Flask app or a MongoDB Flask app and somehow shoehorn

36:25 it in so you can still do these queries?

36:27 You don't have to completely rewrite your data access layer, but make it async friendly.

36:32 Easy?

36:33 Not easy?

36:33 I think it, in all honesty, it requires a bit of effort.

36:36 Yes.

36:36 You'd have to, you'd probably want to choose a different driver.

36:40 So you need to spend some time looking into it.

36:42 And then you'd probably have to rewrite a bit to actually make it work.

36:45 So yeah, I don't think it's all that easy.

36:47 It doesn't sound easy to me either.

36:49 Although it may be worth it.

36:50 It's definitely not going to be just a throw an async and a wait keyword here and there and

36:55 just go with it, right?

36:56 Indeed.

36:56 I think it is.

36:58 It does look to be worth it.

36:59 If you, there's some articles by Magic Stack and the way they talk of the performance they

37:05 can get through the async stuff.

37:06 It looks really, really good.

37:08 Really very fast.

37:09 Yeah.

37:09 I think it's really quite powerful, but it also probably depends, right?

37:13 Like you guys, I suspect have a lot of traffic.

37:15 If you do 10 requests a second on your server or one, maybe just leave it alone, right?

37:24 Pay $5 more and get a bigger server and just be done with it.

37:27 There's probably some threshold where below that it's not worth rewriting it until there's

37:32 enough demand.

37:33 What do you think?

37:34 Certainly, yeah.

37:34 It's, even for us, it's not necessarily worth it to wholesale replace our services with core.

37:42 I think we're only really kind of experimenting with async O systems at the moment.

37:47 Cool.

37:48 So let's compare this a little bit with some of the other, what I consider the Python 3 async

37:53 web frameworks.

37:54 So we've got Sanic.

37:57 We have Jepronto.

37:59 AIo HTTP.

38:01 Like, how do you see your work similar or different to these?

38:07 There's kind of two approaches have been taken here.

38:10 And most of the Python 3 async ones I've seen have been more the micro framework style than

38:16 the Django style.

38:17 I think there's the ones that are Flask-like, which are Sanic and Jepronto, I think.

38:24 And then there's AIo HTTP that I think for very legitimate reasons by design doesn't want

38:30 to go down the Flask approach.

38:31 So I think that's basically the first choice.

38:34 And then I think Quart fits in because it's not Flask-like.

38:38 Hopefully, it is the Flask API.

38:40 So I think that sets it aside and it's kind of the motivation for it.

38:45 So then I think there's kind of that scale.

38:47 And then there's the what is the aim of the project in that scale.

38:50 So Sanic and Jepronto, I think, are all about speed as far as I can understand what they're

38:56 aiming for.

38:56 They're really about trying to get performance out of it.

38:59 And I think AIo HTTP is meant to be kind of like an all-encompassing HTTP library, right?

39:06 So it does the client side as well as the server.

39:08 So I think there's these varying kind of aims for the projects.

39:12 Right.

39:12 And I think it is a massive advantage to say we're not going to create something Flask-like.

39:17 We're going to create Flask effectively, but async-enabled natively.

39:22 And one of the main benefits there is obviously people can migrate more easily to it.

39:27 You already talked about the Flask extensions, all those kinds of things, right?

39:30 To me, it's mostly about not having to learn something new.

39:33 So I did play with Sanic.

39:35 I quite like Sanic, but I didn't want to have to learn how Sanic did things.

39:38 Yeah, exactly.

39:41 And if you want to go on Stack Overflow and ask, how do I do this with my web framework X?

39:46 You'll have way more answers if X equals Flask.

39:49 It's true.

39:49 Definitely.

39:50 Yeah.

39:50 Just the more tutorials, more courses, et cetera, et cetera.

39:53 Although I suspect there's minor differences.

39:55 The majority of it is kind of still the Flask API, the Flask story, right?

39:59 Certainly, there is differences in the details and obviously async.

40:04 Yeah.

40:04 So what are the hangups people might get trying to switch to court?

40:07 Are there things they have to be on the lookout for just due to the async nature or how careful

40:14 they got to be?

40:15 I think one of the most annoying parts is if you have a coroutine.

40:21 Say you want the example you said where you await the request.getJSON.

40:24 If you forget to write the await word, then the data, let me start again.

40:29 If you have data equals request.getJSON in your Flask and you change it to Quartz, that line

40:35 of code will still run.

40:36 It's just that data would now hold a coroutine object instead of the data you're expecting.

40:41 And that could probably catch you up quite easily because until you try and use it, you

40:46 don't know that it's going to be something different.

40:48 What do you mean status code doesn't exist anymore on this thing?

40:52 Like, why did this start failing?

40:54 Why are the attributes missing, right?

40:55 Of course, because you didn't await it.

40:56 Yeah.

40:58 Interesting.

40:58 So let's talk about the deployment story.

41:01 You talk a lot about using G-Unicorn.

41:03 Is it, you said you can basically run both Quartz and Flask apps on top of that in more or

41:08 less the same way, but just different config settings, right?

41:10 Yeah.

41:11 You just change the worker and that should be enough.

41:15 Nice.

41:15 Yeah.

41:15 So you say eventlet is the one to use for Flask and Quart-UVloop for Quart.

41:22 UVloop's pretty interesting.

41:24 Why don't you tell people about that?

41:25 My understanding, which I think is wrong, is that the kind of history goes from libEvent

41:29 to libEvent, which is what eventlet and gvent, I think, are based on, to libUV.

41:34 And they're all improvements on the previous iteration.

41:38 And UVloop is the kind of Python bindings to change the async loop policy to use UVloop

41:46 instead of the default one.

41:47 And the reason you'd want to do this is because it looks like, from all the stuff that's been

41:52 published, that UVloop really does make things run a lot quicker.

41:55 And if I understand it correctly, it's the same kind of base event loop that Node.js uses.

42:03 So it's got some proven track record of being very good.

42:07 So yeah, if you switch it to the UVloop worker, you can really get some performance boosts.

42:13 I get the same impression reading around that UVloop definitely speeds that up.

42:17 And it's cool that it's the same one that Node.js runs on because Node.js, for all of its flaws

42:22 and challenges, is definitely good at handling lots of requests based on IO completion waiting

42:29 behaviors, right?

42:30 Yeah, definitely.

42:30 I find it kind of amusing if I've understood the history right, is that UVloop is a rewrite

42:35 of EV, LibEV, LibUV is a rewrite to work on Windows.

42:40 At least that was the original motivation.

42:42 But as I understand it, the UVloop kind of bindings doesn't work on Windows.

42:46 So I find that kind of like sad irony.

42:49 That is very ironic.

42:51 It's turned its back on its origin.

42:54 How interesting.

42:56 So have you talked with Armin Roedeker, the guy who maintains Creative Flask, about what

43:02 you've done, how it maybe could be contributed back to Flask, what those guys are up to, things

43:08 like that?

43:08 I haven't, no.

43:09 I was actually hoping, because I think you've spoke to him on this program.

43:12 I was going to ask if you could introduce me, which would be excellent.

43:15 Yeah, I'd be happy to.

43:16 Sure.

43:17 Yeah, I don't really know what's going on in official Flask around all this stuff.

43:22 So it seems like you've got a really nice head start.

43:25 So that's cool.

43:26 I don't know what's going on officially for Flask either, but it doesn't look like they're

43:29 actively pursuing AsyncIO at the moment.

43:31 It looks like they're pursuing the release one of Flask, as far as I can tell, which would

43:37 be great as well.

43:38 Yeah, it would be great.

43:38 I suspect making major changes to Flask in terms of APIs and stuff is a slow, tedious,

43:46 careful process.

43:48 Same thing for Django and for Pyramid.

43:50 These are well-established, long-living web frameworks.

43:54 Compatibility is probably a top concern.

43:57 And I think you probably have to make some difficult changes to introduce AsyncIO as well.

44:02 So I guess my hope would be that Quart proves that this is desired and useful.

44:07 And then there's more kind of desire to actually make some Flask changes and to be able to

44:13 merge the two.

44:13 Yeah, then you'll push a major, major pull request over to Flask.

44:17 Well, it would have to be Flask and Virgisk, I think, if it's ever going to be possible.

44:22 Yeah, yeah, for sure.

44:23 Yeah, very cool.

44:25 All right.

44:25 So where are you going in terms of the future for Quart?

44:29 What's next?

44:30 I said earlier, there's details about the API that don't fully match at the moment, which

44:35 obviously I need to get correct.

44:37 So like little details about how e-tags are used and how static files are served, that

44:42 kind of thing.

44:42 After that, I want to kind of like really demonstrate the robustness of the HTTP2 handling and the

44:49 WebSocket handling.

44:50 So luckily, there's some compliance testing projects out there, which I can use for that.

44:56 And then it's also about the development process.

44:59 So Flask and Virgisk have this really nice kind of debug web page that tells you what's

45:03 going wrong when you try and do something.

45:05 I think Quart needs something like that as well.

45:06 Sounds very cool.

45:07 One of the areas of performance that we spoke about was with Async and Await and sort of

45:13 the asynchronous view.

45:15 We also talked about HTTP2.

45:18 How do you see that affecting performance?

45:20 Do you see like the ability to have HTTP2 also adding like another layer of speedups?

45:25 I do see that it makes things quicker when I test it.

45:29 I haven't published anything yet because I haven't really figured out what a kind of safe

45:34 comparison benchmark is because the cost of opening connections is quite high.

45:38 Maybe you should include that.

45:40 But you could effectively pipeline your maybe 20 connections down one and get a big difference.

45:47 But whether that's fair or not, I'm not sure.

45:49 So I don't know how to talk about it in a fair way.

45:52 It's really precarious to publish benchmarks.

45:56 Yeah, definitely.

45:57 No matter what you do, there's always someone that's going to show you how you're wrong.

46:02 And usually what that means is you're wrong because I use it this way and you're measuring

46:09 it that way.

46:10 And if I used it the way I use it, it wouldn't give you the same results as the way that you

46:15 use it.

46:15 And so your results are misleading or whatever, right?

46:18 And it's just really hard to get something truly representative of what people are doing

46:22 when they're doing all these different things, I think.

46:24 One thing I'd really like to do if I could convince my colleagues is change one of our edge

46:28 servers to be HTTP2.

46:30 And then you'd be able to see quite a big difference in real use cases.

46:34 So that'd be excellent.

46:35 Yeah, well, let us know when it's out.

46:37 I'll definitely point people at it.

46:39 That'd be awesome.

46:39 It would be.

46:40 Yeah, yeah.

46:40 Very cool.

46:40 So one thing I noticed is you have your project on GitLab and a lot of people have their projects

46:46 on GitHub.

46:47 And you've talked about this a little bit.

46:49 So why GitLab over say GitHub?

46:51 Initially, it looks a bit silly because GitHub is way more popular than GitLab.

46:57 And projects tend to be judged by, I think, at least superficially to begin with, by the

47:02 kind of stars and forks they have, which is always going to be low on GitLab.

47:06 But for me, GitLab is open source, which sways a lot of it in my mind.

47:12 It certainly made a difference to the company I worked for when we were starting up, like

47:16 to have such a great open source project we could just use to begin with.

47:20 And the other thing I kind of like is its CI system is really nicely integrated.

47:24 And that works very well for Cort at the moment.

47:27 So it's very easy.

47:28 So Phil, if you want to work on Cort, what editor do you open up?

47:31 I use Emacs, although I think I use Emacs really badly.

47:35 I don't really add any plugins.

47:37 I tend to use it just in the terminal.

47:39 Very vanilla.

47:41 I went through a stage of having it reasonably optimized.

47:44 And I just reverted it all back to plain.

47:47 So yeah, I don't put too much in my editor.

47:49 I probably should.

47:50 But yeah, I just open Emacs and go from there.

47:53 All right, cool.

47:53 And in addition to Cort, which is pip install Cort, right?

47:58 What other notable high-PI packages are out there that you want to recommend?

48:03 I think I'll recommend two that I use in Cort.

48:05 I recommend Flacate, which has certainly caught loads of silly bugs and style issues.

48:11 I think mostly I use it to take away any discussion or uncertainty about the style.

48:17 And my-PI for the same reason of catching the bugs and to make sure the documentation is right.

48:24 Yeah, my-PI is very cool.

48:25 There's a lot of activity around my-PI right now.

48:27 It's really nice.

48:28 For sure.

48:28 All right, so final call to action.

48:31 People are interested in Cort.

48:32 How do they get started?

48:33 Are you looking for contributors to the project?

48:36 Things like that?

48:37 Absolutely, yes.

48:38 It would be great if people could give it a go.

48:40 Open issues or pull requests, merge requests of changes.

48:44 That would be absolutely excellent.

48:45 And hopefully it's easy.

48:47 It's just pip install Cort and give it a go.

48:49 Hopefully it's as easy as Flask.

48:51 It's like basically five-line quick start.

48:54 Yeah, that's really cool.

48:55 And you have a video that's now on YouTube from one of the PyCons, which I'll link to.

49:01 That's PyCon UK, right?

49:03 2017?

49:03 Yep, that's the one, yeah.

49:04 All right, so if people want to watch your video presentation as well, I'll be sure to link to that.

49:09 So yeah, it looks like a really cool project.

49:11 Thanks for creating it and coming on the show to share it with everyone.

49:14 Thank you for the insight.

49:15 Of course.

49:15 Talk to you later.

49:16 Okay, bye.

49:18 This has been another episode of Talk Python to Me.

49:21 Today's guest was Philip Jones, and this episode has been brought to you by SmartKits and Rollbar.

49:26 SmartKits is looking for talented Python developers to build amazing Python 3-based microservices.

49:32 Apply at talkpython.fm/SmartKits and level up your career.

49:37 Rollbar takes the pain out of errors.

49:40 They give you the context and insight you need to quickly locate and fix errors that might have gone unnoticed until your users complain, of course.

49:48 As Talk Python to Me listeners, track a ridiculous number of errors for free at rollbar.com slash Talk Python to Me.

49:56 Are you or a colleague trying to learn Python?

49:58 Have you tried books and videos that just left you bored by covering topics point by point?

50:02 Well, check out my online course, Python Jumpstart, by building 10 apps at talkpython.fm/course to experience a more engaging way to learn Python.

50:11 And if you're looking for something a little more advanced, try my WritePythonic code course at talkpython.fm/pythonic.

50:18 Be sure to subscribe to the show.

50:21 Open your favorite podcatcher and search for Python.

50:23 We should be right at the top.

50:24 You can also find the iTunes feed at /itunes, Google Play feed at /play, and direct RSS feed at /rss on talkpython.fm.

50:34 This is your host, Michael Kennedy.

50:35 Thanks so much for listening.

50:37 I really appreciate it.

50:38 Now, get out there and write some Python code.

50:40 I'll see you next time.

Talk Python's Mastodon Michael Kennedy's Mastodon