Monitor performance issues & errors in your code

#188: Async for the Pythonic web with Sanic Transcript

Recorded on Tuesday, Nov 20, 2018.

00:00 Michael Kennedy: What do web servers do most of the time? They wait. They wait on external systems while processing requests. Think about a standard web request to an ecommerce site where you're logged in. You send a session cookie and request to a URL. It pulls a bunch of items from the database, maybe a Redis cache, and even talks to an external API. And that is exactly the situation asyncio is built for. But to take advantage of it in the Python web frameworks, the framework itself has to support async view methods. That's what Sanic was built to do. On this episode, you'll meet Adam Hopkins, who is leading the Sanic project. This is Talk Python to Me, Episode 188, recorded November 27th, 2018. Welcome to Talk Python to Me, a weekly podcast on Python, the language, the libraries, the ecosystem, and the personalities. This is your host, Michael Kennedy. Follow me on Twitter, where I'm @mkennedy. Keep up with the show and listen to past episodes at talkpython.fm, and follow the show on Twitter via @talkpython. Adam, welcome to Talk Python.

01:11 Adam Hopkins: Thank you for having me. I'm excited to be here.

01:13 Michael Kennedy: Yeah, I'm excited to have you here. I'm really excited to talk about asynchronous web frameworks with you, in particular Sanic. But before we get to that story, let's start with yours. How'd you get into programming and Python?

01:24 Adam Hopkins: I started getting into programming mostly in high school, but I've sort of been around computers all my life. I mean, my dad had computers when I was a kid in the 80s, and I never really remember not having a computer. So I've always been very comfortable ticking around and playing with them. But I think I really got started into programming mainly by, I wanted to build a website. I was interested in trying to put things online, and I started to build some stuff. And I mainly started out working with PHP, 'cause at the time that was sort of the big kingpin if you wanted to put something online, and it was easy to get started.

02:05 Michael Kennedy: Yeah, it's definitely, it's still massive on the web.

02:08 Adam Hopkins: Yeah, it is. I think WordPress definitely helps get people started and is a really good intro and a nice tool for people. And I kind of had my own, I don't want to call them problems, but I felt like every time I was working in PHP, I sort of needed to keep one eye on the documentation because I could never remember.

02:31 Michael Kennedy: Yeah, it wasn't obvious, it wasn't clear, just one of those languages, right?

02:36 Adam Hopkins: Exactly. One time there's a function, and the parameters are needle and haystack, and the next time the search is haydle and neestack, and I could never remember which way, the namings convention, and so I started looking around to try to find something else.

02:52 Michael Kennedy: You're like it's got to be better than this. Other people use other tools. What are we going to use?

02:56 Adam Hopkins: Exactly. And I did know a little bit about Python, and more, just I'm going to write a script and print out hello world type stuff, nothing sophisticated at all. And at the time, this was just about when Django had been released. And Django had been released, and I think Ruby on Rails was out there. So it was a question of, here are these two different ways, and which way am I going to go? And I picked one. And I'm happy that I picked Django, because it led me down a nice path, I think.

03:28 Michael Kennedy: Yeah, and I think that's a pretty, I don't know if it was luck or just good, but it was definitely a good choice that you went that way. I feel like Rails has definitely lost a lot of the favor that it had. I know there's a lot of projects out there that are still built on Rails, but it doesn't seem to have the shine that it did five years ago.

03:47 Adam Hopkins: No, and kudos to the Django team. I think what they're doing is pretty amazing, that they continue to build a great product that's still very relevant and still hugely popular. I don't know, the framework's got to be, what, 15 years old, maybe, at this point? Maybe not quite.

04:03 Michael Kennedy: Yeah, yeah, but it's been around for a while. So that's pretty awesome. And did you practice law for a while?

04:07 Adam Hopkins: I did, I did.

04:08 Michael Kennedy: Tell us about that. That's far from programming, typically.

04:11 Adam Hopkins: Well, yeah, so I didn't go to school as, I'm not a computer science major, or anything like that. Everything that I know about programming is entirely self taught. I went to school, and I graduated from George Washington University in DC. And I went to law school after that. After law school, I practiced law for about five years. And all during that time, I didn't really ever lose touch with programming. But it was a hobby for me. Building things was a hobby and was not my day-to-day job. But in 2014, my wife and I decided that, we were living in Massachusetts at the time, and we decided that we were going to move across the world to Israel. And it was sort of as this life-changing time, and we decided we were going to make this move. And as part of that, we're going to assess our career paths. I felt very lucky that I sort of was able to take what had been my hobby and now turn it into a career. And I think that's a pretty fortunate thing.

05:16 Michael Kennedy: I think it's great. And I definitely think it's one of the real benefits of being in technology. We have so many ways of working that are just asynchronous already. Even when we're together, right, we all use GitHub and Slack or email or whatever.

05:29 Adam Hopkins: Right.

05:30 Michael Kennedy: And you could be 5,000 miles away and still do that, right? That's pretty awesome.

05:34 Adam Hopkins: Yeah, I mean, the team that I'm working with right now, it's a pretty small group of guys. But I'm in Israel. There's a couple guys in Ohio in North Carolina, Brazil. The team is, we exist as a team, we function day to day as if we're a team and interact like it's the guys sitting right next door, but really, he's literally half way around the globe.

06:00 Michael Kennedy: Yeah, that's pretty awesome. Maybe that's a good segue about what you do day to day. Are you doing, what do you work on day to day?

06:08 Adam Hopkins: Sure, so my day job. I work at a company called Matrix Retail. Their main product is geared towards retail companies that have a lot of different...

06:20 Michael Kennedy: Like Macy's or some, Target, or something like that?

06:22 Adam Hopkins: Exactly, the type of companies that exist in malls across the U.S. and they're mostly U.S. based. The idea is they're constantly getting all this data. How many sales they had, how many transactions, how many people are walking in and out the store. And we sort of take all of that data and try to make a projection for what their sales are going to be. And then once we have that information, we can create a schedule for their staff. So we determine, you're predicting to have $1,000 worth of sales during this particular hour on this particular day, and how many people do you need to have in the store to be able to reach that $1,000 of sales, but not have too many people that you're just paying for people to stand around doing nothing.

07:13 Michael Kennedy: Interesting, so you're kind of like load balancing, but for the...

07:16 Adam Hopkins: That's actually a great way to put it, I like that.

07:20 Michael Kennedy: There you go.

07:22 Adam Hopkins: You know I think one of the bigger complications in that is it's not just a matter of, how do you slop people into the right place, but then you've got to deal with jurisdictional laws. One place, a shift might need to be, can't be shorter than three hours. And in another place, there can't be two shifts that are closer together and they need to have meal breaks and they have to be a certain length. So all of these sort of validation rules we have to take into account. It makes for a pretty complex puzzle.

07:50 Michael Kennedy: I'm sure it does, actually. Anytime law gets involved it's crazy, as I'm sure you know better than I do. Is this in Python?

07:57 Adam Hopkins: The application itself has a pretty big stack of a bunch of different technologies. A lot of it is in Java.

08:03 Michael Kennedy: Like most places, right?

08:04 Adam Hopkins: Yeah, it's true, it's true. A lot of the stack is in Java, the earlier stuff. What I've been working on is creating sort of a new API on top of what's currently existing to get some more features, get a little bit more performance, and actually that's a lot of how I came into using Sanic, but we can get into that a little bit later. But all this new stuff that I'm adding is all in Python and load balanced across a pretty big cluster using Kubernetes and some other fun tools like that.

08:41 Michael Kennedy: Sounds like you're using a lot of cool technologies. I think it's really cool you're using Sanic for that microservice bit. So you said we're switching to Python, because it's...

08:52 Adam Hopkins: Yes.

08:53 Michael Kennedy: For speed and to make it fast, in addition to adding features which I think is, so many people who are not deeply involved in Python, like, oh, Python's slow in this parallels and we can't do this and whatever, and here you are choosing it for, and it's working out well I would imagine.

09:07 Adam Hopkins: Yeah, I think anybody that's sort of a Java developer out there is yelling at me saying how could that possibly be true. But it is, where we can benchmark some of the stuff that we've got in Java versus some of the stuff that we have running now and we're definitely running a lot faster, a lot leaner. Our memory usage is a lot lower, so we're able to handle a lot more connections and a lot more request-response cycles a lot faster, which is the most important thing at the end of the day.

09:37 Michael Kennedy: Yeah, absolutely. It's so interesting, there are so many aspects to performance in Python. I so don't want to go down that rabbit hole, but you've got the concurrency, you've got C and Cython implementations, you've just got all these things that are really interesting. And then you throw in the web app, where web apps are mostly talking to external systems, and are not themselves computationally heavy. But they're more orchestration in a sense, right? So there's just so many cool things, and it sounds like you have a cool architecture going there.

10:07 Adam Hopkins: What I was just going to say is I'm at point, to get that performance it's sort of figuring out what are the right tools that you need to be able to achieve that. We're using Sanic to sort of power our microservice architecture. But we're also pushing stuff off to a queue, a task queue using Celery, if you're familiar with that tool. It's basically we've got a server sitting somewhere else, and we're just pushing messages off to it and letting it sort of handle all these background tasks, and sort of creating these big large queries and running them in Postgres, we're caching a lot of stuff inside of Redis. So it's sort of like a top to down, top to bottom approach of where can we make things faster.

10:53 Michael Kennedy: Right, and using the right architecture, like asynchronous queues, for example, can just make all the difference.

10:59 Adam Hopkins: Yeah, absolutely.

11:00 Michael Kennedy: Yeah, so maybe this is a good place to start talking about some of the web frameworks and some of these ideas, and before, so Sanic is a web framework based on Python's async event loop, asyncio, and async and await keywords, and all that. And we'll get into all those details, but before we dig in, not everybody is using these newer features. Asyncio only came out in 3.4. And then really I think properly in 3.5 with async and await. So maybe, just tell us what is asyncio, and why is it really good for web apps?

11:32 Adam Hopkins: Sure, well first, I think, you know, everybody should be maybe using it. Maybe that's too broad of a statement. Asyncio is a great standard library module that was added to sort of be another answer to how can Python achieve concurrency. So we have some of the other modules that exist including multiprocessing and threading and stuff like that. And while this answers some of the same questions, it does it in a sort of different way.

12:05 Michael Kennedy: Yeah, when you think of these other, all the traditional ways we're doing it, we've got multiprocessing, we've got threading, and then of course like APIs built on top of those, like execution pools and whatnot. But all of those involve some sort of forking. But asyncio is, inherently, doesn't even necessarily use multiple threads or processes at all, right?

12:24 Adam Hopkins: Yeah, it's sort of a simpler methodology and clear methodology in that you're still running everything on a single process, but you're basically just taking the idea of start working on something, in our case in a web framework it's a request, and you start working on end processing and you get to a point where you say, well, I need to go find some information somewhere else, so I'm going to pause here, and while I'm pausing here, let's go see what else needs to get done. Actually it's very much sort of like what we're, what I'm doing in my day to day job. We're trying to figure out, you've got an employee and he's coming in for a five hour shift, and you want to figure out how do you keep your person employed during those five hours without having them standing around just doing nothing, wasting time and money. They're there to do, to work, so let's figure out how to make your web workers produce the most that you can get from them.

13:23 Michael Kennedy: Yeah, absolutely. The whole I/O bit of asyncio, right, it's right there in the name, is what it's for. It doesn't help you for computational things at all, right? You really only have one thread and if you start doing computational, you know, you're just doing them in order, right. But for web apps, their main job is to talk to a database, talk to other microservices, talk to a queue. Then they just wait, right. So it's the perfect time, the perfect way to structure these web apps is to say, all the time that you're waiting on these external systems, we're going to go do other work, other requests. That's basically the idea behind Sanic, right?

14:00 Adam Hopkins: Absolutely. Using async, it's not like a magic ball that's going to make things run faster. It's going to make things run more efficient. I think that's a better way to put it.

14:09 Michael Kennedy: Yeah, it's not going to be just idly waiting around, it's going to be working on something else. It's a really good multi-tasker I guess. Better go back to this stuff. Alright, so, Sanic is one of these async Python web frameworks. But there are others. What are some of the other ones that people might compare to if they're shopping for this type of framework?

14:30 Adam Hopkins: The ones that first come to mind are aiohttp, that's another big one. Japronto, you mentioned them. I think they've sort of stopped working on developing on that project so I don't know if that one still, with the status of that or not.

14:46 Michael Kennedy: Yeah, I remember it coming out around the time as Sanic. I feel like it did stall out, I'll have a look.

14:51 Adam Hopkins: It's a really interesting project and I think what they did with it is a great approach. It's a little bit different than Sanic's approach because I believe Japronto is written a lot in C, which makes it extremely fast but also makes it a little bit more difficult for I think the everyday high thumper programmer to pick up and look under the hood and make some changes where they might need to.

15:16 Michael Kennedy: Yeah, and looking at the Repo, it's 50% C, 50% Python, and it had a ton of contributions and work on it back up 'til April 2017. Then, you're right, it kind of just went on pause. This portion of Talk Python To Me is brought to you by, us. Have you heard that Python is not good for concurrent programming problems? Whoever told you that is living in the past because it's prime time for Python's asynchronous features. With the widespread adoption of async methods and the async and await keywords, Python's ecosystem has a ton of new and exciting frameworks based on async and await. That's why we created a course for anyone who wants to learn all of Python's async capabilities, Async Techniques and Examples in Python. Just visit talkpython.fm/async and watch the intro video to see if this course is for you. It's only $49 and you own it forever, no subscriptions. And there are discounts for teams as well. Any others that people might consider?

16:20 Adam Hopkins: Quart is another one. I think you've talked with the guy that built Quart on your show before.

16:25 Michael Kennedy: I have, yeah, Quart's really nice as well.

16:27 Adam Hopkins: And I've actually, I've been in contact with him a little bit. He's given us some ideas on where we can add into some stuff, ASGI, to Sanic, and we can talk a little bit about that later. But he's been a pretty helpful resource. I think that one more that I want to mention that's out there, I think the newest on the scene, it's called Vibora.

16:49 Michael Kennedy: Yes, Vibora looks very interesting as well. Although I haven't done anything with Vibora.

16:53 Adam Hopkins: Yeah, I haven't tried it out to see what it is. It does a little bit more than what I would personally like from a web framework, from a micro framework. I like to handle a lot of things like caching and templating and stuff like that myself. It comes with a lot of that stuff. So for a lot of people that might be a plus, so that's another one to definitely put out there.

17:15 Michael Kennedy: Yeah, it's interesting. I tried to play with it a little bit and for some reason it kept crashing, not in the sense that you're doing something wrong, but it would just, like, the process would die. And I was a little underwhelmed with it I guess. But definitely looks like it has a lot of promise. It maybe just needs a little polish. So let's get back to Sanic, which seems to me, maybe along with Quart, one of these async modern Python web frameworks that you really can use and depend upon.

17:46 Adam Hopkins: Like I mentioned earlier, I'm using it now in production. It's definitely production worthy and operational, and it's pretty reliable. In itself, it's still a pretty new framework. But anything that's going to be in this field is going to be fairly new because, like you said, asyncio hasn't been a part of the standard library since version 3.4.

18:07 Michael Kennedy: Yeah, and it's really 3.5 where it actually became, has the proper keywords to make it easy.

18:13 Adam Hopkins: Yeah, exactly.

18:14 Michael Kennedy: Easy to program. So maybe just give us the quick flyover of Sanic.

18:17 Adam Hopkins: Sure. It's probably good to mention that Sanic does use those keywords, so it's Python 3.5 and up only. I know there are some asyncio tools that you can still use 3.4 with, but you're not going to be able to do that with Sanic, so 3.5.

18:34 Michael Kennedy: Yeah, I think most things have 3.5 if they have 3 at all, all these days. How do you feel about this Python 2, Python 3 thing? Do you feel like we've kind of, or downhill headed towards 3?

18:44 Adam Hopkins: Down.

18:44 Michael Kennedy: 3 and beyond?

18:45 Adam Hopkins: Yeah, it's done, it's over. Game, set, match.

18:49 Michael Kennedy: Yeah, I kind of feel the same way. So, this framework here, for example, you have to have not just 3, but 3.5, so cool, so, depends on that. It's a lot like Flask, right?

18:59 Adam Hopkins: It is, and I think the original idea behind it, I'm not the guy that developed it. The person that developed it is a guy named Michael Hill, and he originally created the API and it looks very much like Flask where you've got decorators and these blueprints so that you can sort of nest your functionality. And it looks a lot like Flask, but obviously being asyncio, it operates differently.

19:26 Michael Kennedy: Yeah, absolutely. So, for example, all the view methods can be async def instead of just def, right, and you can use await in them and so on. Do you have to use, does everything have to be async, or can you do like blocking work in there as well?

19:42 Adam Hopkins: Well you could for example, create a function that is a regular def and doesn't have to be async. It certainly would be better to because that's where you're going to be able to sort of see its performance benefits, because by definition, just about everything we do in Python is, there's going to be some blocking involved. So you can't avoid blocking code at all. But by using a tool like asyncio, you're giving yourself some, an opportunity to alleviate.

20:13 Michael Kennedy: Yeah, exactly, so I'm guessing, what I was getting at is, well what if I'm using some library and it doesn't support asyncio, let's say Mongoengine for example or something like that. I could still make those calls in there, it would just be taking less advantage of its async potential, right?

20:30 Adam Hopkins: Exactly, exactly, so, if you're going to use an ORM, say SQLalchemy or peewee or something like that, you could use it and it will still function. But what's going to happen is when you make that call and it goes to make that network call to your server, it's going to block up your whole process and you're not going to be able to start handling a new request during that time frame. Now there are ways that you can get around that and use some of these other tools that are async enabled.

20:55 Michael Kennedy: Right, so the primary thing that you would do, the first thing that you might do is say, I'm using library X to talk to, let's say, Redis. I would be to go out and say, is there a asyncio enabled variant or extension, or something to that library, right?

21:13 Adam Hopkins: Absolutely.

21:14 Michael Kennedy: So talk us through that.

21:16 Adam Hopkins: Sure, I think one of the first things that I do when I need to answer that question is, are you familiar with these awesome lists that kind of circle around Github.

21:26 Michael Kennedy: Yes, Awesome Python and whatnot, things like that?

21:29 Adam Hopkins: Exactly, I like to go take a look at those. There is one for asyncio stuff, and it lists a number of different ORMS and stuff like that that are on there. I do a lot of work in, a lot of work in Redis and there's two different packages in Redis that async and enable Redis that I like to use. One of them is called aredis, where if you did pip install redis, and you get the very popular library that's out there to interact with redis, and this pretty much mirrors the exact same APIs that you just put aredis instead of redis. And the second one is called aioredis, which also has an async enabled API to be able to access your redis layer.

22:12 Michael Kennedy: Yes, so I think many of the different packages people are out there using, there is some variant or some alternative that they can switch to. But I just bring it up because it's not like having an asynchronous method, view method, is going to just magically make your SQLalchemy call, or your MongoEngine call, or whatever, just start being concurrent. You're going to have to go and get a library that's going to allow you to write await this query, await that insert, et cetera.

22:41 Adam Hopkins: Yeah, and I think if you're going to get started in doing work in a framework like Sanic, or one of these async enabled, one of the first things I think you should do is figure out how you're going to talk to your data layer, and figure out which one of these tools work best for you. There's a number of 'em and they're growing every day. I feel like I continually get to see new ones. I think it's an exciting time for Python.

23:03 Michael Kennedy: I think it absolutely is. I think the async and await stuff is really unlocking a lot of the potential. I'm especially excited for Sanic and some of the similar projects because while we've had these cool keywords and this capability, it's not until you can actually create asynchronous web methods that you can actually really break that thing loose and use it where it makes a lot of sense.

23:27 Adam Hopkins: Yeah, absolutely.

23:28 Michael Kennedy: So, it's an exciting time for sure. So what are some of the ways that we might get around these dependencies? So, step one, go and find another library that does the similar of same thing that you're doing but has coroutines and async methods and whatnot. That's option one. If for some reason that fails, is there a way you can use threading or multiprocessing or some kind of background queue or some, is there something else I can do to not lock up my web asyncio loop and still work with them?

23:57 Adam Hopkins: The question is, could potentially get really messy. You're going to take and start a new, say a new thread inside of your loop.

24:07 Michael Kennedy: Could be done. Should it be done, that's a different question. So you could have like a background thread that's always running for each worker process, and then like a queue that you drop something off, an in-memory queue, you drop something off and it picks it up, say, subscribe this person to a mailing list. For some reason, the API that does that doesn't support asyncio. I think for the most part that would work, but it's definitely not super good, is it?

24:33 Adam Hopkins: I think if the goal is to do something like that, I would highly suggest upgrading to Python 3.7, and then you get to use asyncio queues, which is a fairly new feature. Basically, you can just start pushing off tasks to this asyncio queue and let them handle it. And I think the great use for that is I take a request, I'm processing it, I need to send out an email, push that off to this queue and be done with it. A little bit more involved method. Like you said we could do something with threading, or use another package like sari, which is something I've had good success using for a number of years and I'm still using it inside my asyncio stack because it really solves a good problem for me.

25:20 Michael Kennedy: Yeah, it does. So we got our web code, we're writing our async methods, we're awaiting all these calls. Everything is working pretty well. Beyond that it looks quite a bit like Flask, which I think is really a good thing. There's a couple of things I'd like to maybe call out that are either less well-known about Flask and as relevant here as well, or disenables, and one of them is Blueprints. So typically when people say, I'm going to go create a Flask app, they all, they say but what you do is you create the app.py, and you allocate the app, and then you use the app, you know, as a decorator app.route, and you put it on these methods. Boom, you're done with Flask, right? And that's cool for a demo, but if you have hundreds of view methods broken up by categories of, like, here's the store views, here's the user management views, and here's the reporting, and, you don't want to cram that all into one file. And so, Flask and Sanic both have this idea of blueprints to help you partition your projects into different pieces. Do you want to talk about that?

26:18 Adam Hopkins: Sure, I think there's a big difference between looking at the tutorial that's got headlines and views about how to get started, and wow, it's amazing. Hello world, it's such an easy thing. But then you get to the question of, how do I actually build something, how is this usable?

26:34 Michael Kennedy: Yeah, yeah, how does not become a giant mess of one huge app.py file, right?

26:40 Adam Hopkins: Maybe you like one giant file, maybe that's your, but, you know...

26:45 Michael Kennedy: But if you don't, then it becomes a bit of a challenge of juggling that app.route stuff around.

26:52 Adam Hopkins: Exactly, and I think it's, something to do is, okay, you've decided you're going to do async, you've decided on your data access layer and which one of these tools you're going to be using. And then the next question I think is, how am I going to organize this? Am I going to have one Sanic server that's going to handle all my requests, or maybe I need to have 10 different services, and they're all going to be connected and they're all going to be responding to different things. And within each one of those things, maybe we need to have one little unique entity, called a blueprint, that's going to handle certain information. And maybe you need to have 10 of these. So, for example, maybe you're going to have authentication be one blueprint. And maybe you're going to have users be another blueprint. And the idea is just to organize and structure your code. One thing that Sanic also has is what's called a blueprint group, which does exactly that. It just takes a bunch of blueprints and it groups them together. And one of the big benefits here is for routing purposes.

27:56 Michael Kennedy: Yeah, that's cool. Basically instead of saying app.route, you say blueprint.route, and you kind of reverse it. In your main starter file you import all the blueprints and you register them with the app, and say here's a whole bunch of subparts of my web framework and their routes and their view methods, put them altogether here, and then run it. The group sounds pretty interesting, I didn't know about that.

28:19 Adam Hopkins: Yeah, and one of the big other benefits you get with blueprints, is you don't have to be passing around your app object all over the place. So if you're going to have...

28:28 Michael Kennedy: Yeah, and that gets really tricky, yeah.

28:30 Adam Hopkins: Exactly, so it gives you a way to sort of register these views to something. And then when you finally have your app and you're creating your app, they all get added on at that point. So you won't need to create these crazy global variables or anything like that.

28:46 Michael Kennedy: Yeah, I think it's a really nice way to break apart different parts of Flask or Sanic apps into little sections that are more self-contained and then you can import them into the main app.

28:56 Adam Hopkins: It's sort of a personal preference of mine. I don't know that I necessarily say, this is the way you have to do it, but I like to have each one of my different endpoints sort of be like its own blueprint, and at max, have two or there different methods that are attached to it. I like to keep things as organized as possible. And mainly that's for development purposes, it's when I've got an issue or I need to make an update or fix a bug or something like that. It's a lot easier for me to navigate when I've got everything very finely grained, broken up into different files.

29:30 Michael Kennedy: Yeah, that's the same way I do it. And I really like it. You know, if you're in this file, this is the type of stuff you're working on, and just that. So another thing that Sanic makes pretty easy is websockets, and that's been traditionally a little tricky in regular Python code, right, 'cause it'll just block up, how are you just going to wait on this bidirectional communication thing, right?

29:54 Adam Hopkins: I think one of the great benefits that we get from asyncio, asides being able to have these non-blocking access to other services and stuff like that, is to be able to do stuff like websockets, and websockets are fun. It's great when you're building a front-end application and you can have that bidirectional control between server and client. And the way that it's achieved right now inside of Sanic is by using another package that's called Websockets. So you could just do, pip install websockets, and that gets you to the same bit of code that's running inside of Sanic. And Sanic is just basically putting on another routing layer on top of that to be able to get to your socket.

30:39 Michael Kennedy: Yeah, that's really cool, really cool.

30:41 Adam Hopkins: So that now, coming next year, there may be some ideas on how we change that and make that a little bit better, and being able to handle different deployment strategies. And by that I mean using ASGI, which is sort of the asynchronous response to WSGI.

31:02 Michael Kennedy: Right, WSGI is the Web Service Gateway Interface, which is how most things like Flask, Pyramid, Django, et cetera, plug into larger web servers, like uWSGI or Gunicorn or something, right? But the fundamental problem with that API, is it's a single function per call request that has no asynchronous concepts around it, right?

31:24 Adam Hopkins: Exactly, so you're not really going to be able to run an asynchronous application off of WSGI. And this is a problem that the guys building Django channels, I believe, were the first ones that up with the ASGI implementation or specification or whatever you want to call it. We're looking into to adopting that which will open up a greater deployment strategy for Sanic.

31:51 Michael Kennedy: Yeah, that would be really nice if there was the WSGI equivalent for all these different frameworks. That's really one of the key missing things to unlock a lot of it. But since you brought it up, maybe we should talk a little bit about deployment on this. So what is different, or how does deployment for regular web servers and web apps change if you're deploying something based on Sanic versus Flask?

32:14 Adam Hopkins: The biggest change is you could just do python app.py and just run your application straight from inside Sanic. And Sanic has its own way to be able to operate as a web server and interact. So you could just do that and you don't have to do anything more. There is some other deployment strategies where you could use Gunicorn, and there's a worker layer that sort of enables Gunicorn to be able to run Sanic. So we've got that in the documentation, so you could do that. I think the best way to do it, and my personal preferred way right now is to just allow Sanic to handle its own, handle itself and create docker containers. And the docker container's job is to run python app.py, and then stick in front of that something like Nginx, or another load balancer that handles the incoming request and just kind of spits out my traffic to one of my services.

33:15 Michael Kennedy: Yeah, that makes a lot of sense.

33:17 Adam Hopkins: You don't have to do it that way.

33:19 Michael Kennedy: Yeah, sure. Would you set it up as system daemon type thing, system control and like a service unit to run the Sanic part or do you just do that? If you're using docker containers, maybe it's just you start the container and it does its thing.

33:33 Adam Hopkins: Yeah, you could that, but, and this is again, my own personal preference bleeding in here, but docker is such a widely popular tool now. It makes it so easy to deploy and to take something from development on my machine and to stick it up on a staging server and then eventually stick it into production. I find docker to be a very useful tool for that, and one step further is to use Kubernetes to kind of manage that whole process. But that may be a little bit too complex for a lot of people and I don't necessarily want to say that that's how you've got to do it.

34:10 Michael Kennedy: Right, and regardless of whether you're using docker or you're just running it in a separate process, you still probably want something like Nginx which is going to handle SSL, it's going to handle delegating back to that server, it's going to handle, would you serve the static file through there still, through Nginx?

34:28 Adam Hopkins: Yeah, absolutely. I think that's a rather good point to make. And while you certainly could handle static files through Sanic and Sanic does give you an API to be able to do that, your best bet, most of the time, is going to be, let Nginx handle your static files. You're going to get the most performance by just serving those directly. You don't really need to let Python handle static stuff. You don't, let that handle all your dynamic content.

34:52 Michael Kennedy: Yeah, that makes sense. That's the way I run my stuff as well. Let's talk about testing. So testing regular web apps is pretty straightforward. It still requires some tricks to know how to create a dummy request and things like that. But then you just call it and then you get the answer. And then you assert things about the answer. But with Sanic, what is testing look like, because typically testing aiosync stuff means there has to be an event loop, it has to be running, you've got to wait for it to be done, there's like extra stuff. What does testing one of these web apps look like?

35:25 Adam Hopkins: Yeah, it's certainly not an easy thing to do. And I think this is one of those other areas where maybe you've checked off all those boxes and you're now onto the stage of how do I start testing the stuff. And I would say don't try to invent the wheel yourself. Go see what else is already out there. And personally I like to use Pytest. There's a great plug-in for Sanic called pytest-sanic, which solves a lot of these problems and makes it pretty similar to what you're used to doing with Pytest, where you're creating your application and it's a little bit different because you've got your async and await keywords but it's going to handle creating the loop for you. So you can just go about your process the way you expected.

36:11 Michael Kennedy: Yeah, it's pretty interesting some of the stuff they do. So your test method might be an async method, right, async def test something, not just a regular test method, but the Pytest-Sanic knows how to run it, and it's all good, right?

36:24 Adam Hopkins: Yeah and there's also an asyncio pytest, I forget the name of the package that does sort of the same thing, just not in the confines of Sanic itself. But that's also definitely something to look out for. So the answer is yes, its definitely possible. There's a little bit more complexity to it, but take a look at what other people are doing and I think it's a piece of the puzzle that's been solved. So you may as well go see what other people have done.

36:51 Michael Kennedy: And especially since there's a Pytest plug-in, it's pretty easy. So that was one of maybe, I don't know if you'd call it an extension exactly, but it's one of the things that is in the category of extensions. But there's actually a whole bunch of different extensions, for like OAuth, and Sessions, and CORS and all that. You want to give us a quick flyover of some of the more important extensions?

37:12 Adam Hopkins: Sure. I think probably the most important extension that's out there is the pytest-sanic Sanic extension because it gives you the extension ability. Another one that is hugely popular is Sanic OpenAPI, which adds, I don't know if you're familiar with Open API or not, but it's a specification for how do you define your API endpoints. And with this plug-in, you basically just add some more decorators around your methods and it's going to create the output for you that's going to meet your specification of what kind of methods does it accept and what kind of intake. Variables might need to get passed into the method, and eventually what's the output, what does your endpoint give you?

38:01 Michael Kennedy: Yeah, that's really cool. So it defines the API and gives you some Swagger UI back as well so you can actually see what methods are available and things like that, right?

38:09 Adam Hopkins: Exactly. Another one, and this one's going to be a little bit of a self plug because it's my extension, is an authentication sanic-jwt, which adds web tokens and if you do, pip install sanic-jwt, it gives you a blueprint and has all the authentication and points that you would need to log in, to create a token, to verify a token, to pass refresh tokens, and all that kind of fun stuff.

38:38 Michael Kennedy: That's a good one. I see that it has Jinja2 as one of the extensions. What is the default template engine in Sanic?

38:47 Adam Hopkins: Well there isn't one. And this is sort of where it's a micro framework, where it's not going to make a lot of the decisions for you. So you can kind of create your own stack. And personally, I think, while you can serve static files, I think sort of the place where frameworks like Sanic really shine is using them to power web APIs and websockets and stuff like that. Personally I don't use it to handle html, while it is certainly capable of providing html responses, Sanic is going to say, lets you figure out what's the best way that you want to be able to template those and if it's Jinja2, there's a plug-in that you can use for that.

39:32 Michael Kennedy: Yeah, that makes sense. Are there any other template languages that you can use? Can you use Django ones, can you use Chameleon, Mako?

39:39 Adam Hopkins: Yeah. I don't know it would work with Django ones. Anything that's going to work in Python, you know you can use that because at the end of the day what you need to do to respond with Sanic is you just need to be able to pass it a string. So use whatever template engine you want.

39:55 Michael Kennedy: I see, so you would basically just create the thing, and tell it, here's your template file, here's your model data, render it, and have Sanic return the html. So you kind of plug in that layer.

40:06 Adam Hopkins: Exactly, you could use whatever you needed to there, it would work.

40:10 Michael Kennedy: Yeah, it seems like one of the first things you'd want to do is create your own little decorator that takes a template file and then just automatically does that and you just wrap up, wrap up your view methods, right? Don't do that more than twice.

40:23 Adam Hopkins: Creating decorators is a huge thing I think in frameworks like Sanic and Flask and stuff like that. There is some information and tutorials on how you would go about that when you realize that you're going to need something like that.

40:36 Michael Kennedy: Right, yeah, very, very cool. So one of the things that happened, not too long ago maybe a year ago, is Sanic moved from being this framework created by, you said Michael Hill.

40:50 Adam Hopkins: Yes.

40:51 Michael Kennedy: Created by Michael Hill. And has now become more of a community project. Do you want to talk about that whole process, what that means for Sanic and why, why it was done?

41:00 Adam Hopkins: Sure. I kind of got involved with Sanic, first, mainly because it was something that I just wanted to just check out. One of the things that I like to do in my spare time is see what else is out there that I don't know, and try playing with it. And Sanic was kind of how I started playing around with these async enabled frameworks. I sort of realized that there was a missing hole, and this was before I was involved as one of the maintainers of the Sanic project. I saw where there was a hole for authentication, so I created the JWT extension. And then when I started using it for work, I sat down and I said to myself, okay, this is a pretty new framework. You know one of the risks with something that's new is how long is it going to last, and how long is people going to be maintaining it?

41:51 Michael Kennedy: That is really one of the concerns with any one of the frameworks that you mentioned at the beginning, is they're all so new, but web apps are often long lived. You don't want to build your thing on, if you'd built it on Japronto, you'd be really bummed that contributions stopped a year ago.

42:06 Adam Hopkins: Yeah, absolutely. When I go to Github to find some package that's one of the first things I do, is look to see how active it's been, 'cause if I'm going to integrate this into something that I'm putting into production, if it stops getting maintained, then I need to step up to the plate and start maintaining it if something breaks.

42:24 Michael Kennedy: Yeah, and do you want to do that, right, you don't want to do that for everything out there. Sometimes you just want to use something.

42:30 Adam Hopkins: Exactly. And when I was making that decision with Sanic, I said to myself, okay, what happens if these guys stop maintaining it? And I looked at it and I said, okay, if that happens, I realize this is a risk, and maybe I'll get myself, maybe I'll take it under my own wing and just fork it and do what I need with it. And, low and behold, some time goes by and I start noticing that all these issues are piling up and there's pull requests that are piling up, and the release cycle is a little bit slower than it had been at first. And I started talking with some other people that I know that are users of Sanic, and I noticed that they also had the same concerns, and they were saying, well, okay, maybe this is a long-lived project or not. So I started talking with these people and I started talking with the guys that were the maintainers of the project and everybody that had used the package and had worked on it, really liked it and was enjoying doing it, and nobody just wanted to see it go away. So we said, okay, let's take this package that was on Github as a repository for just a single user. Let's turn it into a community package, a community organization and invite whoever wants to be involved, let's get everybody together, and start working on this together. Let's make decisions together. Let's not have just one person hold the key to this. We're still in this process of figuring out what that means to be a community run organization. It's not like we have this answered yet, but, we've been doing this for I guess about, about five or six months now, and it's sort of been a fun ride so far and I think we're headed in a good direction.

44:17 Michael Kennedy: It looks like a good move in that you definitely have some folks involved there. I think it's very positive. I actually just checked out Vibora, and it looks like it's stalled out as well. It's looking more and more that Sanic is the one that has the momentum of the ones that we've been talking about.

44:34 Adam Hopkins: I don't want to be the one to make that claim, but.

44:37 Michael Kennedy: You let me say it. But I think Quart is still doing really well, and aiohttp as well, but some of the other ones that came out at the same time, I kind of feel like, I dunno, they just. They had a lot of activity but not so much. So it's good that you're taking these steps to try to solidify that basin and not just make it dependent upon one person's interest in pursuing this sort of tweak of the Flask API, right?

44:59 Adam Hopkins: Yeah, absolutely, and we've sort of tried to make sure that there are some people that are involved, that have been involved in the project since its beginning early days, and some people that are just getting involved now. We try to have a mix of as many voices as we can, so we have consistency and we're not breaking any backwards compatibility issues. But I'm pretty excited because there definitely has been a good response and been a lot of community action in pushing this project.

45:29 Michael Kennedy: Yeah, that's great. So what are some of the plans going forward? You guys decided to issue the zero versioning style, and go with something different for versioning, right?

45:39 Adam Hopkins: The current version is 0.8.3, and that's going to be the last zero versioned release. Coming up in about another month, we're going to switch over to calendar versioning. And the idea behind this is we've got a fast framework, let's make our development cycles so much fast as well, so we can respond to things quickly. So we decided to go with four releases per year. We're going to do one in March, one in June, September and December, with the idea that the December release will be a long term support release, an LTS release.

46:17 Michael Kennedy: That's pretty cool. I think it makes things more clear, because a lot of folks have been involved in open source for a long time and they see 0.12 and they think, oh great, that's fine. But many people, especially coming from the enterprise-y space and the commercial space, they see zero dot something and they're like, oh, it's not ready yet, it's in some kind of beta or something.

46:35 Adam Hopkins: That's sort of what we're trying to achieve with moving to a community organization and moving to having an LTS release is to make this known that this is a safe project to rely upon, and you can use this in production. It's not going anywhere and it's going to perform.

46:51 Michael Kennedy: That's cool. What does a version number look like in calendar versioning? Is it like 2018 dot, or what are you doing there?

46:58 Adam Hopkins: You could do that. What we're going to do is it's going to be 18.12, so that's going to be the next release. And then after that it'll be 19.3, 19.6, 19.9.

47:09 Michael Kennedy: Yeah, and then little patches and stuff would be like dot something on the end?

47:12 Adam Hopkins: Exactly. So if we need to have inside that release cycle, yeah, then it'll be 18.12.1. I imagine that those type of patches, you'll see more of those with the LTS releases because the idea behind that is, we're going to lock in the API, we're going to lock in the feature set in December of 2018. And that's going to be on its own branch inside of Github. And for the next year, if we see any security issues or bugs, we're going to go and we're going to fix those and we're going to push those out up to the 18.12 package. But while we're doing that, we may move on to additional feature sets inside 19.3 and moving forward that wouldn't necessarily get put into that 18.12.

47:57 Michael Kennedy: Yeah, that's really cool. And one thing I really like about this is I can open up a requirements file or I can do a pip list on a virtual environment or something and just look and know, oh, that library's a year and a half old, or that library's pretty new. If I do a pip by list and I see requests that I don't even know where it's at, like, let's say I see pyramid at 1.8.7, alright, that's out of date, it should be 1.9.something, but how out of date, right?

48:28 Adam Hopkins: You need to update it. You need to know the package, you need to be involved in the community and have an understanding of when all those releases are happening to know whether your version is out of date and by how much.

48:40 Michael Kennedy: With your model, it's super clear, oh, that's six months old, we should probably consider upgrading that.

48:44 Adam Hopkins: Yeah, I don't want to say it's our model, it's a thing that's out there.

48:48 Michael Kennedy: Sorry, the model you, let me rephrase that, the model you've adopted, it lets me see that. Yeah, that's really nice.

48:53 Adam Hopkins: And I think pip is now onto that model as well and there's some other. I've noticed a lot more repositories using calendar versioning, I'd say, in the last year, than I had before that.

49:03 Michael Kennedy: That's cool, well I'm definitely a fan. Now, one of the things I saw looking through the documentation is you have, on the various issues and items in Github, is you have help wanted labels. And sounds like you're looking for folks who'd be willing to be contributors and participate, right?

49:21 Adam Hopkins: That's sort of the goal, is this is a community built project, let's get everybody involved that wants to be. And one of the things that, you know I'm not the only one doing this, but one of the things that I do when I check Github and I see that there are new issues open, I try to make an assessment of how difficult would it be to implement this feature, or is this a bug, or whatever it is, and use Github to provide the context because then if I'm new to the project and I want to help out and say, I don't really know where can my efforts go, it's a little bit easier for me to see, okay, well this is an easy task for me to handle, so let me try this one first as opposed to trying to add an ASGI or something like that, which would be more difficult.

50:08 Michael Kennedy: Yeah the community's still working on that one as a whole. That's cool, but if people are keen to do it, there's all sorts of stuff I'm sure they can pick up, even triaging issues, working on documentation, there's tutorials, being a relatively new framework there's probably not as hard or solidified, as say something like Django, to make changes.

50:30 Adam Hopkins: Absolutely and I think the best thing there, if you have an interest in it, the best way to get involved is to just go into Github where we have, we just opened up a community forum using Discourse, and just look to see what the conversations are and just start responding, just put in some thoughts. There's a conversation about something, about something that strikes your interest, put in your own thoughts and it's sort of a good way to just get yourself involved and your face known. So you don't necessarily need to get involved by making a pull request, solving bug issue number 1234. I think just making your presence known is a great way to contribute yourself in your thoughts.

51:20 Michael Kennedy: Yeah, it's cool. A lot of people are looking to get into their first open source project for one reason or another, and I always like to highlight opportunities to do that. It sounds like this is a good one.

51:32 Adam Hopkins: Anybody's looking to contribute, we're definitely, we'd love to have help with documentation and testing. And I think one of the big things right now would be examples, I used Sanic to solve this problem, and this is how I did it, that kind of stuff.

51:47 Michael Kennedy: Yeah, cool, that sounds really great. So I guess we probably, we're about out of time, so we should leave it there for Sanic. Let me ask you the two questions I always ask my guests at the end of the show. If you're going to work on Sanic or any other Python code, what editor do you use?

52:01 Adam Hopkins: I'm a Sublime Text guy. I really like when I can open up my code editor and just see code and I don't like to have too many distractions, so, I like simplicity.

52:11 Michael Kennedy: Yeah, that's cool. And notable package on PyPI, not necessarily the most popular, but you're like, oh, you should know about this, or, oh, go and throw pip install sanic out there, so you don't have to say that one, but maybe something else, like give me an extension or something, or asyncio ORM?

52:26 Adam Hopkins: I think maybe I'll leave all that stuff for the listeners to go and explore and find maybe one of those awesome async, let's do some of that, maybe someone wants to create an awesome Sanic, maybe that's a good contribution.

52:40 Michael Kennedy: That would be cool.

52:41 Adam Hopkins: How 'about as a notable package, I've got another GUI that I don't think you guys have necessarily covered yet.

52:50 Michael Kennedy: We've been on a real kick, but we haven't hit 'em all, I'm sure.

52:54 Adam Hopkins: Well how 'about pip install eel, that's another one that you can, it's a fun little project which is...

53:01 Michael Kennedy: Eel's a good one.

53:02 Adam Hopkins: The idea is somewhat similar to what Electron is doing, but doing it with Python instead.

53:08 Michael Kennedy: Yeah, so Electron JS allows you to take and create what looks like a desktop application, but is actually a hidden chrome running an html defined front end, maybe it's something like Ember or Angular, and then a node.js back end for much of the logic. And so Eel does basically that, but for Python, right?

53:28 Adam Hopkins: Exactly, and one of the benefits, besides giving you the entire Python ecosystem, is it's a little bit less intensive than, say, Electron, might be.

53:39 Michael Kennedy: It's also a little simpler, isn't it? If I remember lookin' through.

53:41 Adam Hopkins: Yeah, it's a fun little project.

53:43 Michael Kennedy: Nice, yeah, people should check that out. Alright, awesome, so final call to action, people are excited about Sanic and async web frameworks, what would you say to them?

53:52 Adam Hopkins: What I'd like to most stress is get yourself involved in open source work however you can. I think especially for people that are maybe newer developers and just getting started out, open source is a great way to get experience and to get exposure, and most importantly start learning. So I'd say, come say hi to us over in the Sanic community, we'd love to have you. But if not, go find another community that you're passionate about and make yourself known.

54:20 Michael Kennedy: And this whole web framework, async web framework in Python, you think it's time has come?

54:26 Adam Hopkins: Don't quote me on this, but I believe eventually Django is planning on adding async, several releases, like, a coupla years, two years down the road. It's coming, it's definitely the way to go. So the future is now.

54:40 Michael Kennedy: Yeah, you spoke about Django channels and I think it was Andrew Godwin that wrote this, put together a cool thing called "A Django Async Roadmap", that lays out their plans for basically doing something similar, and it's a really great article. I'll throw it in the show notes for people who are on the Django side of the fence. But, yeah, definitely I agree with you and thanks for being on the show, Adam. It was great to talk with you. And best of luck on Sanic, it's a cool project.

55:06 Adam Hopkins: Great, thank you very much.

55:07 Michael Kennedy: You bet, bye.

55:08 Adam Hopkins: Take care.

55:09 Michael Kennedy: This has been another episode of Talk Python To Me. The guest on this episode was Adam Hopkins, and has been brought to you by, us, over at Talk Python Training. Want to level up your Python? If you're just getting started, try my Python Jumpstart by Building 10 Apps course, or if you're looking for something more advanced, check out our new Async course that digs into all the different types of async programming you can do in Python. And of course, if you're interested in more than one of these, be sure to check out our Everything Bundle. It's like a subscription that never expires. Be sure to subscribe to the show. Open your favorite podcatcher 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. This is your host, Michael Kennedy. Thanks so much for listening, I really appreciate it. Now get out there and write some Python code.

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