« Return to show page
Transcript for Episode #129:
Falcon: The bare-metal Python web framework
00:00:00 Michael Kennedy: Full featured web frameworks are great, but sometimes living closer to the network layer is exactly the thing you need. This week, you'll meet Kurt Griffiths and John Vrbanac who work on the Falcon web framework. It's a bare metal Python web API framework for building very fast application back ends and microservices. How bare metal? Well, other frameworks like the Hug REST framework are even built upon Falcon. This is Talk Python to Me Episode 129, recorded July 17th, 2017. 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 This episode is brought to you by Linode and Rollbar. That's right. Welcome to Linode, who has joined Talk Python to Me as a major sponsor. Be sure to check out what both of them are offering during their segments. It really helps support the show. Kurt, John, welcome to Talk Python.
00:01:16 Kurt Griffiths: Hi. Thanks for having us.
00:01:18 Michael Kennedy: Yeah. It's great to have you guys here. I love talking about web frameworks, and yours looks very, very interesting, so I'm super excited to share it with all the listeners. But before we do, let's start with your stories. Kurt, how did you get into programming in Python?
00:01:31 Kurt Griffiths: Well, so just programming in general, I got started way back in the day in sixth grade. I was in my math class, and they had, I think it was probably a Commodore or a PET, one of those old crafty machines. And someone, one of my classmates, just came in one day and said, "Hey, check this out." And he brought up the BASIC terminal and he typed in this little program, and all it did was convert dollars to, I think it was to yen. Nothing fancy, but I'd never seen someone program before like that. And that just sparked my interest, and I thought, "That's so cool. You can type incantations into the keyboard and the computer will do something for you." So that's how I got started.
00:02:13 Michael Kennedy: Yeah. That's really nice. And it probably didn't seem that unachievable. You're like, "How did you do that? Oh, that's all? Well, that seems pretty easy. I could do this." Right?
00:02:20 Kurt Griffiths: Yeah, exactly. It was very approachable. And I think probably a lot of people got introduced to that, to computing. So this was back before a lot of people had computers at home, and some of their first interactions with them were at school. So yeah, I went home and I did a little research. I found out that not long after that, my dad brought home an old surplus IBM XT from work, and I found out it had BASIC on it. And so I saved up my allowance for a few months, and much to the astonishment of my sister, who thought I was wasting all my money, I spent it all on a programming book. And then the rest is history.
00:02:55 Michael Kennedy: That's awesome. What was the first language? What was that book on?
00:02:59 Kurt Griffiths: Yeah, BASIC was the first language. I went on from that to, I think it was Turbo C++, and I've learned a lot of languages on the way. So I've actually been doing Python probably for about five years now.
00:03:12 Michael Kennedy: Nice. How did you get introduced to Python?
00:03:14 Kurt Griffiths: So that was after I came on board to Rackspace and we were doing some projects there, and we'll probably talk about this a little bit later, but I was actually doing a lot of C++ at the time. And we had done some C++ on the server, but that's not very fun, I have to admit. So we were looking for something a little more friendly for that environment, something a little more easier to work with, and settled on Python. A lot of other people were using Python at Rackspace, and so it was a natural choice. And I started doing some Python things.
00:03:47 Michael Kennedy: Yeah, that's cool. Did you miss the curly braces and the semicolons?
00:03:51 Kurt Griffiths: It took some getting used to, I must admit, but now I have the opposite problem. I go back and I keep forgetting the curly braces.
00:04:06 Kurt Griffiths: Yeah, there you go.
00:04:07 Michael Kennedy: How about you, John?
00:04:08 John Vrbanac: Yeah. So I actually got started programming whenever I was a little kid. My father was a electrical engineer for Cisco Systems, and whenever I was seven, he introduced me to DOS and BASIC. And I quickly outgrew that and then ended up in Borland's Turbo C++. So that was my first taste of a real language I like to say.
00:04:35 Michael Kennedy: Yeah, yeah. C++ is a good language.
00:04:38 John Vrbanac: Yeah, especially back in those days. It was the days of no Stack Overflow and spending a bunch of time in the Barnes and Nobles or Border's in the programming language aisle.
00:04:51 Michael Kennedy: Back when you had to earn your knowledge.
00:04:53 John Vrbanac: Yes, yes.
00:04:55 Michael Kennedy: That's right. So how did you get to Python?
00:04:57 John Vrbanac: So interestingly enough, I've been getting involved in a lot of Linux work. So it had been what? 2012-ish, and at the time, I was having to do a bunch of build scripts and whatnot and using Gradle, and I got really tired of doing a lot of that, and I was like, "Well, a lot of people use Python." So I started getting interested in that just from a scripting perspective, and then I think it was App Showdown or something or whatever it was called back then, and thought, "Well, let's try to write an app." And I think that was the worst Python I've ever written in my life, but it got me very interested in it.
00:05:35 Michael Kennedy: Oh, yeah. That's really, really cool. All right, so what do you guys do day to day? Kurt, I'm guessing from the background, you might be at Rackspace right now?
00:05:43 Kurt Griffiths: Yep. So I've been at Rackspace about nine years. I'm actually going to be leaving pretty soon here to work on some startup ideas. So I've done a lot of stuff at Rackspace. Most recently, I've been on working on, well, what we call managed security at Rackspace. So we provide a lot of security services for customers. We take vendor products and we wrap them up and glue them together and make them easier to use and provide a 24/7 SOC that's staffed by security analysts that watches our customers' servers, makes sure that we keep the bad guys out. So been working in the security space probably for about three years now.
00:06:22 Michael Kennedy: Yeah, that's really cool. So what stuff can you guys do to keep hackers out? Like for me, I make sure I patch my code straightaway as soon as any security updates come out, I try to follow best practices and stuff. But beyond simple guidance, how do you detect these things?
00:06:42 Kurt Griffiths: Yeah. I mean, there's certainly just the basic stuff that you need to do, the patching, et cetera that you mentioned. The Center for Internet Security publishes a number of hardening guidelines a lot of people follow, especially if they need to get PCI compliance or something like that. So you do your basics. Beyond that, the attackers have become so sophisticated these days that it's really not when they're going to get in. I mean it's not really if they're going to get in, but when.
00:07:10 Michael Kennedy: Not if, but when.
00:07:11 Kurt Griffiths: Yeah, so that falls into, how do you monitor constantly? How do you react quickly when you detect an anomaly and you've got to get in there and diagnose it and find out, well, maybe it's just you changed something in your application, so it's fine, it's just behaving a little differently? Sometimes maybe, yeah, someone is in there and you need to throw down the firewalls and get them out. So it's a combination of the hardening, the patching, but also the constant 24/7 vigilance and having a response plan in place so you can react quickly and resolve the issues.
00:07:48 Michael Kennedy: Yeah. I'm sure you have some pretty amazing stories, but let's stay focused on Falcon. John, how about you? What do you do day to day?
00:07:56 John Vrbanac: So I actually work in Rackspace managed security, as well. I'm relatively new to the team. I previously came from EMC and then previous to that actually was in a different part of Rackspace, so jumping around the industry a little bit.
00:08:12 Michael Kennedy: You live in the cloud?
00:08:14 John Vrbanac: Yeah, pretty much. I think a lot of what I do day to day is help build some of the utilities that we use internally to manage security, so helping in delivery of some of the products and helping isolate what and how we're using our tooling.
00:08:35 Michael Kennedy: Yeah. It sounds cool. Like I said, I'm sure you guys probably have awesome stories that would be really fun to dig into. But let's talk about your web framework, Falcon. It looks really neat. Kurt, you want to tell us, what is Falcon?
00:08:47 Kurt Griffiths: Well, so just basically, it's a little Python web framework for building microservices, proxies, application back ends, those sorts of things, with a particular emphasis on low latency, high scale use cases.
00:09:00 Michael Kennedy: Yeah. So one of the ways you describe it as a bare metal Python web framework. So yeah, that gives you a sense. It's not Django with its admin back end and that. It's something way more low level and closer to the network, right?
00:09:14 Kurt Griffiths: Yeah, definitely. So it's a relatively thin layer on top of WSGI, so there's not a lot between you and your web server. And it also tries to embrace HTTP versus paving over it so you have direct access to everything that's going on.
00:09:31 Michael Kennedy: Yeah, that's cool. Headers, requests, response, all of that, codes, all of those things are super natural on there, yeah. Yeah, so people listening, WSGI web service gateway interface, the standard that almost every Python web framework implements to be a Python web framework and plug into the high performance web servers. So you have a nice little problem for people to solve, math problem on your homepage. It said, "You've been unburdening cloud apps for over 4.53 times 10 to the negative second centuries." So been around about 4.5 years or so. Is that right?
00:10:07 Kurt Griffiths: Yeah, that's about right. I kind of stole that from, Ars Technica used to have a little thing in the early days when they were getting started, they used to have a little thing on their website, so I have to give them credit for that.
00:10:19 Michael Kennedy: Yeah, that's nice. Yeah, so why don't you guys both tell me, John, you first, what's your relationship to the product, or to the project? Did you both start it together, one of you started it first and the other came on later?
00:10:33 John Vrbanac: I got involved in Falcon a little bit further into its history. I suppose my initial use was purely as a user. I was using it to solve several scaling problems from just a general sense from a performance standpoint. But overall, what attracted me to Falcon was the non-opinionated ideas, being able to have a flexible base that I could build upon and reuse over and over, almost like a framework for frameworks if that sort of makes sense.
00:11:09 Michael Kennedy: That is interesting. I think there's people who learn web development or do web development are kind of in two camps. They either like all this flexibility and like, I'm going to pick this ORM or this data access layer and this caching layer. And I'm going to plug this together like that, or they want just, it's kind of slotted together. Like this style or Django, right?
00:11:32 John Vrbanac: The interesting thing about Falcon is that it, in some ways, a lot of frameworks are very, have a very opinionated way of approaching things, which is both good and bad. I think Kurt and I like to talk about how Falcon is in the middle of a spectrum of frameworks. All of them have different and important purposes. But yeah, in some cases it depends on either your own personal opinions or the problem you're trying to solve. But in the case of Falcon, it just seemed to jive with the way that I thought, the way that I approached problems. And fast forward a couple years later, then became a maintainer, so.
00:12:17 Michael Kennedy: Yeah, that's cool. Wait a minute. I'm creating this thing. I was just using it. What's going on here? How did I get here?
00:12:25 John Vrbanac: Yeah.
00:12:26 Michael Kennedy: Exactly. So Kurt, what was the original idea behind creating Falcon?
00:12:32 Kurt Griffiths: Well, that's an interesting story. So Falcon was actually based on an experimental framework that I had developed four plus years ago. So at the time, my team and I were tasked with building a cloud backup product for Rackspace, and the way this would work is you would have these headless agents that would sit on the cloud servers. They would do the heavy lifting, the backup, the encryption, uploading to cloud files, which is Rackspace's version of OpenStack Swift. And you need some way to command and control the agent. So it needed to be able to talk back to this sort of monolithic web application that had the control panel there. It had some of the control logic, the settings, what you should back up, et cetera, how often you should do it. So we needed some simple messaging mechanism for those two things to talk, the agent and the control plane. So we started looking at some different ways to do that. Based on some previous experience, we were biased towards using the REST architectural style for that messaging service. As I mentioned earlier, so I had played around with Node.js, but that wasn't really, it was still pretty new at the time and not a lot of people were using it at Rackspace, but Python seemed to be more of a natural fit. So I started just looking around. I'm a firm believer in using the best tool for the job. I just looked at some of the different frameworks. Flask seemed like a natural fit. It's a little bit lighter weight than Django. It looked like it might be a good way to go. So I just built a very basic kind of hello world app, as you do, and just benchmarked it. So the thing with this, we had some very stringent latency requirements because one of the things that you would do through the control panel is actually go browse your remote machines, their file systems, and choose what you wanted to back up. Right? And so that message passing had to be semi realtime. We wanted it to be as responsive as possible. So I had an idea of the budget of response time. I don't remember exactly. It was certainly under 200 milliseconds, probably more 120, around there. Ideally, we wanted to get it down--
00:14:41 Michael Kennedy: Does that count ping time?
00:14:42 Kurt Griffiths: Yes, overall, so you need under 200 milliseconds for it to feel real time, that's just the nature of humans, which means that your app, you don't have much left over for your app to turn around requests. Honestly, at the end of the day, you have like 40, 30, 20 milliseconds to turn around a request, because a lot of the time is just eaten up with traversing the Internet. So yeah, so I didn't have a big budget for latency there. And I benchmarked just a simple Falcon app, and it was much higher than I expected. Honestly, I don't recall the exact time, but I thought, "That's kind of weird. "Let me just try just a straight up raw WSGI app." So I looked up the PEP 333 and wrote up just a simple app there that didn't do anything, and there was actually a huge gap between that, the response time there, and just with a Falcon hello app. And I thought, "That's weird. "What is Falcon doing?" I mean, "What is Flask doing?"
00:15:38 Michael Kennedy: You mean Flask, yeah, yeah.
00:15:40 Kurt Griffiths: "It's taking so long." I have to confess a sin here. I used to do a lot of Ruby, as well, Ruby on Rails stuff. And I remember the Sinatra framework, which is one of the early projects to explore this microframework concept. So I did the same thing over there. I did a basic, just raw rack app, and then I benchmarked Sinatra, and just the difference between those wasn't actually that great. And so I just had the hypothesis that there's probably stuff that's, I could probably make a thinner framework than Flask that would give me enough to work with that I don't have to just code directly to the WSGI interface but it's still fast enough for my needs.
00:16:23 Michael Kennedy: Right. You just need a few abstractions.
00:16:26 Kurt Griffiths: Yeah.
00:16:27 Michael Kennedy: You need body, you need the verb, you need the headers, and you need JSON serialization or something like that, right?
00:16:33 Kurt Griffiths: Exactly. So it just started out very simple, kind of as this experiment, very pragmatic. And I end up building this little framework and we put that service into production and it worked really well for us. And so I decided we probably should make this real, and so we started the Falcon project, started inviting people from the community to try it out and contribute, and then that's where it came from.
00:16:57 Michael Kennedy: Yeah, okay. That makes a lot of sense, definitely in the benchmarks, which we'll talk about performance later. It's faster than many of the other well known frameworks.
00:17:06 Kurt Griffiths: Yeah, definitely. And going back to what John was saying, it's there when you need it. Not everybody needs it, so it just depends on what your use case is.
00:17:14 Michael Kennedy: Yeah, for sure. So John, maybe this is an interesting time to do a compare and contrast. What's this framework, like how is it different and special than, let's say, Flask, Django, Pyramid, these types of frameworks?
00:17:26 John Vrbanac: Inherently, the more pragmatic approach, I believe, for Falcon is the benefit. But to what I was saying earlier, a lot of this does tend to be very much of a spectrum and a part of the, you could almost think of it as the wider community of frameworks. The direct benefits, yes, you've got performance, but sometimes performance is not always the most important thing. I heard someone say once, and I firmly believe this, that in a lot of cases, time to market is more important. So yeah, you could have the most performant app in the world, but if you don't get the job done, then--
00:18:06 Michael Kennedy: We'd all just be optimizing our stuff in assembly and making it go really fast.
00:18:10 John Vrbanac: I think there are some interesting differences. A lot of them do come around the more either heavier or opinionated type, say, almost workflows. The benefit that Falcon has given me personally and one of the reasons I got very interested into it is that, especially when you're building internal tooling, often in cases, you need a certain level of flexibility that oftentimes opinionated frameworks don't give you. So they might be great for 80% of your work, and then it's a real burden to try to get through. It doesn't mean that those frameworks aren't any worse or better, it's just different tool for the job. So to what Kurt was saying, trying to focus on the right tool.
00:18:58 Michael Kennedy: Yeah, absolutely.
00:18:59 Kurt Griffiths: What we've seen in talking with users is it's really become a complementary framework to what people are doing. You'll have someone who has a large, monolithic Django app, and they have a really hot endpoint. Maybe they're using Django REST framework, which is a great framework, but for this one end point, it's just killing their servers. So they carve that piece out, write it in Falcon. It lets them make the performance trade offs that they want to make. And then they keep the rest in Django and then they're able to partition that one piece off into a microservice, and that works well for people. So that's just one example of how it works alongside these other frameworks.
00:19:37 Michael Kennedy: Yeah, that's a much better answer than saying, "Let's rewrite it in Go. Let's rewrite it in C++. Let's rewrite it in," name your other language and technology that has its own problems, right?
00:19:48 John Vrbanac: I think something very important to go along with that, a lot of people, they do focus on those initial numbers. Those numbers may look great, but real world numbers are often much different than benchmark numbers. And at the end of the day, a lot of this, especially in today's world, it's about how you can scale your app after the fact. You may only have a couple thousand requests a second out of the gate, but whether or not you've architected your system or your application to be able to scale to 100,000 or a million or whatever, then that's usually the most important. 'Cause in this day and age, hardware is scalable.
00:20:28 Michael Kennedy: Yeah, for sure, and you definitely, the benchmarks, they're often doing something very, very lightweight inside, like reading a value off the query string and returning it back as JSON or something. And I think maybe the way to think of it is more like this is the overhead this framework adds to your app, but that's not your app. You know what I mean? Your app is actually the talking to the database, the logic, this other service it calls, et cetera, et cetera, right?
00:20:54 Kurt Griffiths: Yeah. I mean, if you recall the little story I told, the core idea around Falcon is, our promise to you is we will remove the bottleneck in the framework. Your performance bottleneck will not be in the framework. Everything outside of that is up to you, right? And it depends on all the things you're doing.
00:21:12 Michael Kennedy: That's still your problem, yeah.
00:21:13 Kurt Griffiths: Yeah, but at least you don't have to worry about that piece.
00:21:16 Michael Kennedy: Yeah, it's really cool, and I definitely want to talk a lot about the performance, but a little bit later. Falcon is mostly for building APIs, if I understand it right. Is it possible to build like Jinja2 or Chameleon-based UI type things, or is it really just focused on APIs?
00:21:35 John Vrbanac: You can. I know several of our users have actually asked how to do these types of things, and it's actually not that hard to plug in a Jinja2 rendering system into your resources for Falcon. Is it really well designed for that? Not really. You could easily build something around Falcon to do it. Often in cases, I think the majority of our use cases are mainly around APIs, but it doesn't mean you couldn't.
00:22:03 Michael Kennedy: Sure. And I think it's cool to have a framework focused only on APIs, 'cause you know what? It's really probably super good at that compared to trying to fit it into all these other models.
00:22:14 John Vrbanac: One of the things that, I was talking with a user, this was quite a ways ago, maybe in December, and they were actually building effectively a little ORM on top of Falcon, just to give you an idea of what the scope of, how much somebody was able to change it, which is, that's awesome that they're able to do that. I would imagine it would be a little bit more work than what people would often want to do. Maybe another framework is a little bit more well suited for it. But in their case, they wanted the flexibility and control.
00:22:45 Michael Kennedy: Sure.
00:22:45 Kurt Griffiths: So probably the people that would do that sort of thing are your control freaks, right? They want to tweak everything.
00:22:52 Michael Kennedy: Control freaks in the developer space? Never.
00:22:56 Kurt Griffiths: We're happy to accommodate them.
00:22:58 Michael Kennedy: Yeah, that's cool.
00:22:59 Kurt Griffiths: Love to do it.
00:23:01 Michael Kennedy: This portion of Talk Python to Me is brought to you by Linode. Are you looking for bulletproof hosting that is fast, simple, and incredibly affordable? Look past that bookstore and check out Linode at TalkPython.fm/linode That's L-I-N-O-D-E. Plans start at just five dollars a month for a dedicated server with a gig of RAM. They have 10 data centers across the globe, so no matter where you are, there's a data center near you. Whether you want to run your Python web app, host a private Git server or even a file server, you'll get native SSD's on all of the machines, a 40 gigabit network, 24/7 friendly support, even on holidays, and a seven day money back guarantee. Want a dedicated server for free for the next four months? Use the coupon code Python17 at TalkPython.fm/linode You guys had said that it's almost like a meta framework in some sense. It's low that you could actually build other frameworks on top of it in a sense. Does that land in that space, this example?
00:24:01 Kurt Griffiths: Yeah, definitely. We've seen a couple people do it. Hug is probably one of the better known frameworks that's actually built on top of Falcon.
00:24:09 Michael Kennedy: Interesting. Hug is built on top of Falcon. I was going to ask you about Hug and Django REST framework and API Star. Those three, well, API Star and Hug feel like the newcomers that are also trying to be API focused only, whereas Django REST framework is more extending Django than being a new thing, I think.
00:24:29 Kurt Griffiths: Yeah. I think the Django REST framework is trying to just be an easy on ramp to APIs when you're already doing your work in Django. So it's a natural place there. I think Tom has done some good work with that. You look at Hug, API Star, those sorts of things, they're trying to ease the burden of API development. When you do that, you necessarily have to be more opinionated. We've mentioned this earlier. Falcon tries to be a little more low level, less opinionated, but you can certainly build these kinds of things on top of it.
00:25:00 Michael Kennedy: Yeah, that's really cool, actually. And it's interesting to hear about the Hug relationship. So speaking of the philosophy, you had three basic driving points for your philosophy, to be light, fast, and explicit, right?
00:25:13 Kurt Griffiths: Light we've touched on a little bit, being fairly low level, but there's a couple things that we haven't mentioned, some of the benefits that gives you. One is that you have a smaller code base, so it's easier to make a reliable framework. When something blows up, it's somewhat straightforward to find the problem and fix it quickly, which becomes very important in large scale deployments. When you're serving many, many thousands of requests, you have hundreds of servers, you need to be able to diagnose problems very quickly. So that helps, as well. And it also helps just optimize it. It makes it easier to do that, as well.
00:25:53 Michael Kennedy: Yeah. One of the ways to optimize code to make it go really fast is to just not run code.
00:25:58 Kurt Griffiths: That's right.
00:25:58 Michael Kennedy: Right. If this part would be slow, let's just not do that in our framework.
00:26:02 Kurt Griffiths: Do less work.
00:26:03 Michael Kennedy: Yeah.
00:26:03 Kurt Griffiths: That's the name of the game. What else? So fast, and we've touched on that a bit before, as well. We try to be one of the fastest frameworks. I don't think we necessarily claim to be the fastest framework for all benchmarks and all scenarios, but we try to be up there at the top, and again, make sure that you don't have to worry about, you don't have to spend time looking for the bottleneck in the framework. You are in control of making those performance trade offs outside of that piece.
00:26:32 Michael Kennedy: Yeah, for sure. And the third was explicit, right? Like keep the magic out.
00:26:36 Kurt Griffiths: You can do a lot of clever things with thread local storage and inversion of control and things like that. We do a little bit of IoC. But we try to make it painfully obvious where the data is coming from, where it's going, how to set the data, all those sorts of things, because again, especially in large scale deployments, when you're trying to debug an issue, you don't want it to be doing something surprising.
00:27:04 Michael Kennedy: Yeah, absolutely. The more obvious, the better there. So the opposite of Ruby on Rails or these other frameworks that have lots of conventions and stuff just happening and you got to know it, right?
00:27:15 Kurt Griffiths: Like anything, there's a trade off. You get a lot of power from that, but there is a lot of magic and a steep learning curve there.
00:27:22 Michael Kennedy: Sure. So John, what do you guys do for reliability and testing? Do you have continuous integration set up, things like that?
00:27:31 John Vrbanac: We do all of our testing via Travis at the moment. Well, everything except for, I would say, our performance benchmark testing. But we test against all of the major versions of Python at the moment, and that would be currently all the way from 2.6 to 3.6 at the moment.
00:27:51 Michael Kennedy: Yeah, that's great. That's quite the span.
00:27:55 John Vrbanac: We do claim support for both versions of PyPy, and we had a Jython job in there, but I think we were running into a few problems with Jython. But beyond that, we actually, we mentioned Hug earlier in. We do have an integration test with them, which is very helpful. So I think we do run quite a few automated tests against Falcon, and we try to make sure and maintain backwards compatibility at all costs.
00:28:28 Michael Kennedy: Yeah, that's something I really think is highly important in the web space, because you see a new update come out. You pip install upgrade, push that to the server. If that breaks something, you don't want to be caught out. "Why is the website down? Oh my gosh, this is really upsetting."
00:28:47 Kurt Griffiths: Just a quick thought on that. This came about because in some of our extremely high scale cloud services that we built at Rackspace, we were relying on some web frameworks that would tend to break in subtle ways just in minor version releases, so we felt that pain very keenly, and we wanted to be very careful about that. So that's core to our change management philosophy around Falcon, that we go to great lengths not to break stuff without a major version rev.
00:29:21 Michael Kennedy: Yeah, absolutely. John, it seems like the philosophy ties into that, as well, the less code you have, the more lightweight, the more explicit, the easier it is to test and not break things.
00:29:31 John Vrbanac: Definitely. Even when we do add in more substantial features, like we have a feature coming in for 1.3 around media content type handling, so handle serialization, deserialization, all of that, but all of that is 100% optional. And it's very important that it is that way and that it does not impact request times from a performance perspective, and we go to great lengths to make sure that things are architected in the way that somebody can feel comfortable just upgrading to the next minor version and not worry about breakages.
00:30:12 Michael Kennedy: Yeah, absolutely. I think that's great. Unless you turn on that feature, it's not going to affect you, right?
00:30:17 John Vrbanac: Or in this case, it's there, it's just you have to start using it. It's how the feature was designed so that it doesn't impact you unless you use it.
00:30:27 Michael Kennedy: Yeah, that makes a lot of sense. So Kurt, I have a challenge for you. It's a little bit difficult to describe code in an audio format, so don't go too much into detail, but give a sense of what it's like to program for this. If I want an API that exposes customers or whatever, I can get a customer and create a customer, what does the code that I put together to do that look like?
00:30:53 Kurt Griffiths: I typically start out thinking, 'cause I'm a REST geek, so I start thinking about the resources. So I'll think about, "Okay, we need to define a customer." By resource, this represents a customer. And then maybe resources represents a list of those customers. We'll just think about that. So in Falcon, you represent a resource with just a plain Python class, nothing super special about it except that in that class you have what we call responder methods. And these are just methods that have a well known name that starts with on and then underscore and then the name of the HTTP method that you want to respond to. So this resource responds to get requests, post requests, et cetera, so you create a method for each one of those. Within that method in that class, you'll receive request and response objects. And so this isn't too dissimilar from maybe a WebOb sort of a workflow where you get request and response objects that you deal with. It is a little bit different in that we hand you a response object, so you're not constructing it yourself and returning it.
00:31:55 Michael Kennedy: You fill it out?
00:31:56 Kurt Griffiths: Yeah. So like in Flask, you'll say, you'll instantiate some kind of response and return it. We'll pass in a pre-instantiated response, which actually saves you some work for the use cases that Falcon is designed for. You're typically not returning different types of responses for different types of, say, JSON versus HTML. Your negotiation is done a little bit differently. So you pull the information you need out of the request, do what you need to do. You may use like SQLAlchemy to go talk to the back end, pull out a response, and then you fill out this HTTP. You've got the status code, you've got the headers, things like that. And because this is a class and you instantiate it upfront, you can pass in a database connection or whatever, anything like that. There's no magic
00:32:45 Michael Kennedy: Yeah, that makes testing super easy, 'cause it shows you right where to pass in the mock stuff later or other test fake things.
00:32:52 Kurt Griffiths: Mm-hmm. So it's just straight up, normal Python class instantiation. So once you have that filled out, then you need to wire that up to a route. So a route is simply a mapping between a URL and your resource. So you'll have a place in your code, you probably have a setup file that goes through and instantiates your resource class, passes in the database connection, whatever you need to do there, and then you instantiate an API object, which is a WSGI app. And then on that object, you can call .add, add_route and you pass in the URL you want and then the resource. Something else that's a little bit different from some of the other frameworks is that we don't use decorators. We have you set up the routes in a central location. Part of that is philosophical. We talk about Falcon being somewhat unopinionated, but in a few places, we are a little opinionated. We just find that having everything in one spot makes it easier to reason about your API namespace versus having it sprinkled around with decorators all over, but that's just a little bit of philosophical difference.
00:34:06 Michael Kennedy: Yeah, I got to say, I like that as well. You can go to one place near the app startup and go, "Okay, these are the routes. This is where they go." It makes a lot of sense.
00:34:16 Kurt Griffiths: And it just makes things a little easier. Again, it's very clear where things are coming from, where they're going, 'cause decorators can be a little bit, I love decorators, we use them for some things, but sometimes they can be a little magical. Probably the other thing to point out here is that you don't specify what verbs you're mapping to that URL. So we are a little bit opinionated around encouraging the REST architectural style, which means you think about resources, and each resource is responsible for deciding what methods it's going to respond to. And so if you don't implement a method, Falcon will just return a method not found for you, and it's as simple as that.
00:34:53 Michael Kennedy: Right. Okay, interesting. So, I might say, in my example /users I don't know, users or customers, /users let's say, and then that maps to a user resource class, and there might be an on_get and an on_post but no on_patch. So would patch come back as 404 or a different status code?
00:35:15 Kurt Griffiths: It would come back as method not found. So you found the resource. Something exists at the other end of that URI. It just doesn't support the method that you requested.
00:35:28 Michael Kennedy: Right, I see.
00:35:29 Kurt Griffiths: So this is, again, this goes back to the spirit of HTTP and REST and kind of, we've read probably way too many RFCs, so some of that comes out through some of our design choices.
00:35:43 Michael Kennedy: Is there a place I can make it return a teapot?
00:35:45 Kurt Griffiths: Yes, actually.
00:35:46 Michael Kennedy: That's awesome.
00:35:47 Kurt Griffiths: Just for the heck of it, a couple years back, we implemented a bunch of 700 errors just for fun, so they're in there.
00:35:56 Michael Kennedy: Hey, everyone, this is Michael. Let me tell you about Datadog. They're sponsoring this episode. Performance and bottlenecks don't exist just in your application code. Modern applications are systems built upon systems, and Datadog lets you view the system as a whole. Let's say you have a Python web app running Flask. It's built upon MongoDB and hosted and scaled out on a set of Ubuntu servers running NGINX and micro WSGI. Add Datadog, and you can view and monitor, even get alerts, across all of these systems. Datadog has a great getting started tutorial that takes just a few moments, and if you complete it, they'll send you a sweet Datadog T-shirt for free. Don't hesitate. Visit TalkPython.fm/datadog and see what you've been missing. That's TalkPython.fm/datadog. John, there's a bunch of different add ons and templates. So the philosophy of having it very small and very compact is cool, but then a bunch of people have created add on extensions for this, right?
00:36:49 John Vrbanac: Yeah.
00:36:50 Michael Kennedy: Yeah, you want to highlight some of those?
00:36:51 John Vrbanac: Definitely. So the interesting thing about the community and how some of the add ons occur is that a lot of this comes out of a desire for, in some cases, for people's needs, or someone has a specific, let's say they're trying to deal with authorization or serialization, or they have some very opinionated way of deciding how to deal with specifications or whatever the case may be. Falcon does allow for a lot of flexibility and control around those kinds of things. So you can easily extend or wrap aspects of Falcon. One of the interesting things that we did discover through the number of people who were writing, effectively, serialization and validation add ons for Falcon, we ended up taking a lot of that community feedback and compiling that into the features, the media content type handling that's going to be coming in 1.3.
00:37:52 Michael Kennedy: Right, that's the thing you guys were just talking, yeah, that content negotiation stuff that you were just talking about.
00:37:57 John Vrbanac: Yeah. A lot of frameworks have very opinionated ways of looking at it. Myself, I've used quite a few different methods, depending on the needs. Whether it be EMC, Rackspace, personal projects, whatever it may be, everyone has a little bit different ways of handling data or validating it, and so trying to find a uniform and generic enough approach for everyone took quite a long time. But some of that didn't occur until a lot of our community members started writing their own implementations. So it was very cool to see how that evolved from, started as an add on and now it's become something that's going to be baked into the framework and the next release.
00:38:47 Michael Kennedy: So you had like five or six examples of people trying to solve the problem. You're like, "I think we can put it all together like this."
00:38:52 John Vrbanac: Yeah, most definitely.
00:38:55 Kurt Griffiths: We're trying to be careful, 'cause we want to leave space for people to go and innovate and do these third party add ons, and we don't want to be too opinionated. But in cases like these where we've seen a lot of, it's pretty much something everybody needs, we're moving towards a place where we think we're going to start creating some basic, just a basic way to get started out of the box and completely optional and still allow room for other people to build more sophisticated solutions, but just to give people something to get started with.
00:39:25 Michael Kennedy: Yeah, of course. So are you thinking these are extra packages that you can pull in, or are they going straight into Falcon, the package?
00:39:35 John Vrbanac: They are all, I believe all of these are usually directly on PyPI. Some people have just avoided that stuff, and they just build them into their almost pseudo-frameworks that sit on top. I know for several smaller projects that I've done, I've had a very specific problem, and I built middleware to solve the problem, and it was never upstreamed or anything like that, but it was great to solve the problem for the business need, and it was carried around. But there's no sense in upstreaming a lot of that. But I think most of our contributors have just pushed up a PyPI package that starts with, "Falcon-" and then whatever they wanted it to be.
00:40:19 Michael Kennedy: Yep, all right, that makes sense.
00:40:20 Kurt Griffiths: is a great example, a common use case for APIs, to access them from single page apps and things. And so we had someone who's contributed a few patches to Python say, "Hey, should I just build this out, and should we make it part of the framework or not?" And he just ended up maintaining it separately, and I think that's great. I think that's what the open source community is all about. For the stuff that we pull in, I think one of the deciding factors for us is what dependencies it brings with it, 'cause we don't want to force extra stuff on the operators that they have to manage. And also, the more dependencies you bring in, the more likelihood that they might cause bugs or problems. So we try to keep our dependency list super short. So if it's something like JSON, like the built in media handling helpers that John was talking about that are going to be in the next release, they just use the standard library JSON.
00:41:17 Michael Kennedy: Yeah, so they don't bring much baggage with them.
00:41:19 Kurt Griffiths: And it's very basic stuff, so it doesn't bring a lot of baggage with it. You start looking at some other things, it starts to get heavy pretty quickly.
00:41:27 Michael Kennedy: Sure.
00:41:28 Kurt Griffiths: So we might, we're exploring our options, trying to still experiment with how we want to do this. I think over time, we'll probably have a few projects under the Falcon rework that we maintain, but we want to make sure we don't stomp on anybody's efforts to do something really interesting out there in the community.
00:41:47 Michael Kennedy: Sure, yeah. Maybe have some cookie cutter template type things that will bring in these additional libraries and make it easy to get started but not make it part of the project itself if people don't want them.
00:41:57 John Vrbanac: Definitely. We've actually talked about that a bit, and I know I've been playing with a few things on the side. I haven't actually pushed up anything yet. But we've had several discussions, especially since PyCon, around how we can make that adoption and building of projects a little bit easier on the community. So hopefully we'll have a better answer around that in a bit.
00:42:24 Michael Kennedy: Yeah, sounds good. So John, let's talk about hosting. You can run Falcon, obviously, on CPython as far back as 2.6 and as far forward as 3.6, so that's really great. It also runs on PyPy, right?
00:42:39 John Vrbanac: It does, both PyPI 2, like I said, PyPy 2 and PyPy 3. From how Falcon can be deployed is a very interesting, in a lot of cases, it just comes down to what your needs are, 'cause some people, there are still performance differences between 2.7 and 3.5, 3.6, 3.7. A lot of those performance differences in Python have started dwindling, but there are still differences, especially with PyPy 3. There's still some performance disadvantages. So if you needed top performance for whatever you're doing, maybe it's not quite there or ready for you yet. In a lot of cases, while I generally deploy with PyPy, a lot of people that I talk with, they're more than happy with 3.5 or 3.6.
00:43:37 Michael Kennedy: Right, CPython 3.5 or 3.6, yeah, yeah. I guess that comes back to, how much of it is the framework that you're worried about, versus how much of it is your internal code and the stuff you're doing internally? How much can it leverage the speed ups of PyPy versus does that thing you're using have some kind of C speed ups anyway and so it would lose those or whatever, right?
00:43:59 John Vrbanac: A lot of the performance problems that we see are not necessarily in the Python directly. There's a lot of speed advantages you can get by using PyPy, and I highly recommend people using it. One of the difficulties that we see, especially in recent times, is dealing with the WSGI servers themselves. I know this is something that really came up over and in discussions at PyCon between the Django REST framework, so that would be Tom Christie, Falcon, and Flask. We were all talking about how to deal with some of the bottlenecks we see in the WSGI servers. Like I personally deploy with Gunicorn, and I think it's great. But there are certain bottlenecks in the WSGI servers currently. And yeah, you can sometimes squeeze out more performance out of using uWSGI or micro WSGI, but yeah, we run into quite a few problems right now on that.
00:44:57 Michael Kennedy: Well, and then there's the whole async side of the story that doesn't even fit with WSGI, and the HTTP/2 thing, and there's a lot of room for those to get better.
00:45:07 John Vrbanac: Most definitely. And that's actually what Tom Christie from Django REST framework and API Star, that's a lot of the work that he's exploring right now in Uvicorn is to try to deal with this kind of compatibility between async and Gunicorn and trying to resolve some of these WSGI-ish philosophies. So it's very fascinating. I imagine that the story around this will radically change in the next year, which makes me very excited.
00:45:37 Michael Kennedy: Yeah, me too. I think it's definitely a place where there's a lot of flowers blooming, and somebody is going to crack this problem and we'll all just agree on it and it'll be like WSGI. Of course you use that.
00:45:48 Kurt Griffiths: What I really like about Tom's approach is, up to this point, a lot of the attempts to crack this have been monolithic. Like someone will have a web framework, but then they'll build in an async web server, and it's hard to just use their web server and it's fairly opinionated. But building an async worker type for Gunicorn, exploring the way of creating a de facto interface for that, nobody is really interesting in doing a WSGI 2.0, but we need something to code against so people stop implementing it every 10 different ways.
00:46:25 Michael Kennedy: That's right.
00:46:26 Kurt Griffiths: So yeah, we're following his work closely. I'm hoping to contribute once I get a little bit more time here, and we'll see how it goes.
00:46:32 Michael Kennedy: Yeah, that'd be great. It would be really cool to see something come out of that. So another thing around this performance and ways to host and run and work with Falcon is you guys also do something interesting with Cython, right, Kurt?
00:46:45 Kurt Griffiths: Interesting thing with Cython, Cython, if you're not familiar with it, it translates your Python code to CPython code and compiles it for you, just auto-magically. And if you want, you can add additional annotations to your Python code that will, they basically give hints to Cython. So you can say, "Hey, this is an int," or, treat this as an actual array and it's going to be a fixed size," or things like that, and then that allows Cython to do even more optimizations on it. But you can simply compile any Python code. You can run it through Cython, it'll compile it for you, and you get just, the measurements I've seen, around 10, 20 percent decrease in your CPU cycles just by doing that.
00:47:30 Michael Kennedy: That's cool. And that's not compiling your code running on Falcon, but compiling Falcon itself once you've installed it, right?
00:47:36 Kurt Griffiths: That's right. So in the setup.py file, it just detects Cython if you've installed it, so it's an optional dependency. If it's there, then it'll just use the build extension from Cython to pull in all the Falcon. You could certainly do the exact same thing with your own code. It's not terribly difficult. It's kind of a nice, easy way to get a little bit of an extra performance boost if you are invested in CPython. If you can move to PyPy, then you'll see a much greater increase, but not everybody can do that.
00:48:09 Michael Kennedy: Right. That probably depends somewhat on, yeah, it depends on the libraries that run inside your methods. Yeah, cool. So while we're on the topic, let's talk about performance. You guys have some benchmark numbers that you put up there, and I just, I can feel the challenges every time I see benchmark numbers.
00:48:28 Kurt Griffiths: Yeah.
00:48:29 Michael Kennedy: People are shooting arrows at it. "No, you didn't do it this way, but I did it that way. If I do it this way, it's faster." Like yeah, it's just, but I still feel like they're kind of important. They give you a general sense, right? So on CPython, you have, let's see, you've got Flask coming in around 4,200, for this particular benchmark, you lay out the details on the website, like 4.2 thousand requests a second. You've got Bottle going at 16,000, but Bottle is a super micro framework. And then Falcon coming in at 30,000, which is really nice. So use Pecan as a baseline and it's 28 times faster than that, so that's cool.
00:49:10 Kurt Griffiths: Pecan was one of the frameworks that we were looking at for developing some services earlier on, so it just happened to end up in the benchmarks. And we probably need to add Django. It's fairly straightforward benchmark just to show you what's possible, really, how much of a difference it can make to remove some of the layers and the abstractions. And you may end up not needing, some people, a lot of people, are moving to Go or Node looking at some other options, but you may not need to. Maybe you just need to use something a little bit lighter. But we try to be very, very straightforward about what this is and how we've done it. You certainly need to go benchmark your own use cases. For that messaging service that we used for cloud backup I mentioned earlier, I went and built out an almost full implementation in Node and in Python and benchmarked both of them to see which would do better for us. So yeah, you got to do your own homework, but this gives you a rough feeling for what's possible.
00:50:11 Michael Kennedy: Yeah. That's very cool.
00:50:13 John Vrbanac: I was going to say, in addition to that, one of the things that is often missed as a part of the benchmarks that we do is that we do remove the WSGI server from the benchmark, a lot for the reasons why I was mentioning earlier around the performance constraints that occur in the WSGI server. So that's where if we really encourage people to focus on what their use case will actually be, because that's really the numbers that will, at the end of the day, matter. So you can do great things and they can be extremely fast. It just really depends on your configuration and your use case, 'cause the benchmarks that we ended up doing were primarily to illustrate overhead within the frameworks themselves and to show that this is where we sit from a how much load the framework has on the entire process, so the theoretical maximum currently.
00:51:14 Michael Kennedy: Right, yeah. If you did nothing, you could maybe get this. Yeah, and it's also work pointing out the PyPy one, where as I said, the CPython one was at 30,000 requests a second, the PyPy one is 345,000, 346, so that's a really dramatic increase.
00:51:31 John Vrbanac: One of the things, I know I've definitely used PyPy quite a bit in more larger scale caching services, and the higher performance nature of running Falcon under PyPy has really saved the situation, so to speak.
00:51:50 Michael Kennedy: Yeah, that's really cool. Another piece of advice you guys have is to use, is it Micro-JSON, uJSON, if you're using CPython, 'cause it'll speed up the serialization and query string parsing and stuff, but skip it on PyPy, because otherwise it'll have to fall back to C types 'cause Micro-JSON, I'm guessing, is partially implemented at C.
00:52:11 Kurt Griffiths: PyPy has been historically at a bit of a disadvantage when it comes C types. They've been getting a lot better at that, so we'll see where that goes. But if you're using Python CFFI, it works a little bit better, better optimized for that case. This just goes back to the trade offs, right? There are a lot of nice drop in replacement libraries you can use that are highly optimized, but make sure you go and look at actually how they're implemented. They may be faster on CPython but slower on PyPy.
00:52:41 Michael Kennedy: Yep. Measure, don't guess, huh?
00:52:43 Kurt Griffiths: Yep.
00:52:44 Michael Kennedy: John, how about some notable users or deployments?
00:52:46 John Vrbanac: Yeah. We've got quite a few people from various organizations. We have people from LinkedIn that they have a few open source projects that use us. EMC, obviously, since I was there, OpenStack. Wargaming, which for people who are in the gaming world, I believe they did use us for certain aspects of World of Tanks.
00:53:09 Michael Kennedy: Oh, that's cool.
00:53:10 John Vrbanac: Rackspace, obviously. In fact, I think there's a talk that was done by some of the Wargaming people in Russia over some of their usage. Yeah, we've got a lot of people. I think last time we checked, we get, if our stats are anything, I think we get around 50,000 something downloads per month with PyPI.
00:53:33 Michael Kennedy: That's awesome.
00:53:34 John Vrbanac: We have a lot of people using it in various circumstances, a lot of people using it for internal things.
00:53:39 Michael Kennedy: Yeah, and they're not going to talk about it probably.
00:53:41 John Vrbanac: Yeah.
00:53:42 Michael Kennedy: There's also everyone using Hug, in a sense, right, I guess?
00:53:45 John Vrbanac: That's actually one of the very interesting things about Falcon in its current position of having someone like Timothy coming along and building a framework on top of us. We don't necessarily get the advantages from a download perspective, but we get users by proxy almost, which is very interesting.
00:54:06 Michael Kennedy: Yeah. Probably improvements made at your level by way of that framework also help you.
00:54:11 John Vrbanac: Yeah, definitely trying to understand some of the use cases that occur in a framework like Hug definitely help inform some of our decisions, or it helps keep us honest going back to the whole continuous integration. It's great to have almost a classic use case that we can test against.
00:54:33 Michael Kennedy: Yeah, that's really cool. All right, gentlemen, I think that is all the time we have to talk about Falcon, but let me ask you the two questions I always ask at the end, and Kurt, I guess I'll start with you. Favorite editor? If you're going to write some Python code, what do you open up?
00:54:46 Kurt Griffiths: So I live and breathe in Sublime. It's my working horse editor. Years ago, I used to be more of an IDE guy, but I've taken the pragmatic programmer's advice to heart and tried to find something I can use that's a little more versatile and I can use for everything, and then I don't have to context switch with my brain.
00:55:06 Michael Kennedy: Yeah, makes sense.
00:55:07 Kurt Griffiths: So it's a nice mixture between having access to a lot of keyboard shortcuts and whatnot, but I can also use my mouse for certain things that are more efficient that way.
00:55:16 Michael Kennedy: Sure. John?
00:55:17 John Vrbanac: NeoVim. I'm one of those Vim people.
00:55:19 Michael Kennedy: All right, awesome. And notable PyPI package? What do you guys want to recommend? Kurt, go with you first. Oh, sorry. Go ahead, John.
00:55:28 John Vrbanac: One of the ones I like to recommend is the testing dot libraries. A lot of people, they talk about having to mock up database interactions, or when they're trying to test their API, whenever there's a Python package that will actually on demand set up a local instance of Postgres or Redis or MySQL and then tear it down as a part of your tests. And you can eliminate a lot of testing burden just through those packages. So I'm a big fan of them.
00:56:00 Michael Kennedy: Yeah, that's really cool, 'cause sometimes mocking out the data layer is just so much work for that trade off, you know?
00:56:06 John Vrbanac: It is, and you also need to test your migration, so it's great for that.
00:56:10 Michael Kennedy: Yeah, cool.
00:56:11 Kurt Griffiths: We mentioned Uvicorn a few times. That's Tom Christie's new baby, and he's doing some impressive work over there, so I invite everyone to check that out. He uses the MagicStack HTTP parser and asyncio drop-in. Some pretty impressive performance numbers out of that. Some interesting work, so check that out.
00:56:31 Michael Kennedy: Yeah, it's cool. It's based on uvloop as well, right?
00:56:33 Kurt Griffiths: Yes, yep. And probably just another one, I want to give a shoutout to Brian Warner's Magic Wormhole project. I think he gave a talk at it a couple years back at PyCon, but it's just a really clever, easy way to securely ship files from point A to point B to send something.
00:56:52 Michael Kennedy: It's like on demand Dropbox from the command line with a one time code thing.
00:56:58 Kurt Griffiths: Yeah. Whether or not you even care about the security, it's just a really slick way to ship files, so check that out.
00:57:05 Michael Kennedy: All right, yeah. Good recommendations, you guys. Very nice projects. All right, final call to action. If people want to get started, what do they do?
00:57:12 Kurt Griffiths: Go check out FalconFramework.org. It just gives you a quick overview of the project. Has links to GitHub. Has links to our Gitter channel. We have a couple channels there. There's a user channel for just getting started, asking questions. We have a dev channel for contributors. And check that out. Once you get playing with the framework, if you run into any issues, reach out to us. We're always happy to help. And we're always looking for contributors, certainly, of all levels. We're happy to help you get started there, as well.
00:57:46 Michael Kennedy: All right. That's great. Yeah, definitely a cool project to get involved with. A lot of people ask about what open source project they might get a chance to, they want to do their first open source thing, this would be a fun one.
00:57:56 Kurt Griffiths: If you're interested in learning the fundamentals of the HTTP protocol, which is a surprisingly complex protocol, it's a good way to get under the hood and see how it all works.
00:58:07 Michael Kennedy: Yep, it sounds great. Thank you guys for being on the show. This project is really, really interesting, and I'm glad to see it being successful.
00:58:13 John Vrbanac: Awesome. Thank you.
00:58:15 Kurt Griffiths: Thanks a lot.
00:58:16 Michael Kennedy: This has been another episode of Talk Python to Me. Our guests have been Kurt Griffiths and John Vrbanac. And this episode has been brought to you by Linode and Datadog. Linode is bulletproof hosting for whatever you're building with Python. Get your four months free at TalkPython.fm/linode Just use the code Python17. Datadog gives you visibility into the whole system running your code. Visit TalkPython.fm/datadog and see what you've been missing. They'll even throw in a free T-shirt for doing the tutorial. Are you or a colleague trying to learn Python? Have you tried books and videos that just left you bored by covering topics point by point? 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. And, if you're looking for something a little more advanced, try my Write Pythonic Code Course at TalkPython.fm/pythonic. Be sure to subscribe to the show. Open your favorite pod catcher and search for Python. We should be right at the top. You can also find the iTunes feed at /itunes Google Play feed at /play and 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.