Brought to you by Michael and Brian - take a Talk Python course or get Brian's pytest book


Transcript #147: Mocking out AWS APIs

Return to episode page view on github
Recorded on Wednesday, Sep 4, 2019.

00:00 Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to earbuds.

00:04 This is episode 147, recorded September 4th, 2019. I'm Michael Kennedy.

00:10 I am Brian Okken.

00:11 And this episode is brought to you by DigitalOcean. Tell you more about some cool stuff they got coming up later.

00:16 Right now, I want to talk about tables.

00:19 Okay. And really quick, what's 147 in hex? No, just I couldn't do that. I don't expect you.

00:25 Anyway, no, but one of the things I do need to do occasionally, I need to do really fast is generate tables.

00:33 And I know it's not something that everybody needs to do, but if I've got a whole bunch of data, like in this case, some dictionaries list, and I want to be able to just print that stuff out.

00:44 They're all the same type of dictionary. They just hold some data. I want to print them out.

00:48 How do I do that quickly? And for test reports or whatever, I found this called rapid tables.

00:54 And it's really kind of awesome.

00:56 Oh, that is so cool. Wow.

00:59 Yeah. So you just basically, you've got a list of dictionaries, and then you want to print them out, and you want it to look like a good ASCII table.

01:07 It shows you how to do it, and it also shows you how to do it with a thing called term color.colored.

01:13 I didn't know this was around. So you can do colored tables if you want. I probably won't do that.

01:19 It also converts stuff to markdown and restructured text. That's kind of neat. I might use that, but right away, I just need the just really easy tables.

01:27 And it says it's super fast in speed, like speed-wise. It goes fast.

01:32 But what I like is that it's not a lot of extra code gunk in your code. You just kind of say, I want this table, and you can print it out easily.

01:41 Yeah, that's super cool, because a lot of times, like this kind of formatting, it's a lot of Z fill and pad these strings and L pad.

01:50 Like just like trying to get the spacing, or you're putting tabs, but every now and then the tab, like the word is too big, and so the tab will shoot it to the next column.

01:57 It's a lot of work, and this is not you doing that work.

02:00 Right. So for all the columns, you have to go through each of the elements to find the longest one to figure out how wide you need to make the column.

02:07 And yeah, there's other libraries to do this, but this one I think is pretty cool.

02:12 Yeah, it's very cool. I also like the markdown and restructured text output option. Yeah, very cool.

02:17 So if you think about what is like one of the most popular packages, what would you say? Take some guesses.

02:24 Requests.

02:25 It's got to be requests. I don't know if it's the most used one, but it's certainly the one that has people's attention and imagination.

02:31 So what would you think if I told you here is a competitor to requests called HTTPX?

02:39 Ooh, I like the X.

02:40 It's extra cool. Absolutely.

02:42 What's really interesting about HTTPX, though, is it's not a from scratch, like throw away the ideas of requests.

02:49 It's compatible with the request API.

02:52 So what you can do with requests, you can do with this.

02:54 So you can kind of drop it in.

02:55 But it has all these other cool features.

02:58 And in fact, it comes from this project called ENCODE, E-N-C-O-D-E, ENCODE.

03:03 And over there, they have a ton of cool things, including Django, REST Framework, Starlet, UVicorn, HTTPX, and also some other async stuff.

03:12 And a lot of the things happening around here, UVicorn databases, ORM, HTTPX, is all about adding the asynchronous capabilities to these various things.

03:22 And that's one of the shortcomings of requests is it doesn't support an async option.

03:26 But this HTTPX, it does a bunch of cool things around the async space to kind of let you gradually go up the stack of complexity.

03:35 So you can just be straight synchronous stuff doing what request does.

03:39 It has this concept of parallel requests.

03:42 So you can issue a bunch of requests in parallel, but you don't have to think about actually creating the async event loop and stuff.

03:51 You just say, I want to run these things all in parallel.

03:52 And you could even ask it, say, like, I want to process the responses as they come in.

03:57 So I can say, like, while parallel.haspending responses, the response is parallel.nextresponse.

04:03 So I can kick off, like, 20 requests and just process them as they come back in, like, four lines of code or something.

04:10 Oh, that's so cool.

04:11 I like it.

04:11 Yeah.

04:11 Or if you really want, if you're in something like Starlit, where it has actual async support for the method, you do want that to be async.

04:18 So you can actually flip into, like, an async mode where those happen the same way I described, but also on the asyncio event loop.

04:26 So it's really cool.

04:28 Also, some other neat features.

04:29 So, like I said, a request-compatible API.

04:32 It does HTTP2, which, like, I could do a request for a page.

04:36 And as I pull down, like, say, five CS style sheets and a JavaScript and an image, you can go get those all in one request.

04:44 So it has support for that, which is cool.

04:46 It has a standard synchronous interface, but also async and await if you want it.

04:51 And, Brian, you'll like this one.

04:54 It allows you to make direct requests to WSGI or ASGI, the asynchronous variant of that, apps.

05:00 So imagine I'm creating a Flask app.

05:01 I don't want to test it.

05:02 I can actually, inside of my Flask app, provide HTTPX the app, right?

05:08 The Flask app object.

05:10 And then I can issue requests that don't go through the network layer.

05:13 They just go through this test app instance that I've created.

05:16 Oh, that's cool.

05:17 Isn't that cool?

05:18 So if I want to, like, mock out some behavior or, like, interact with it as if I were externally, I don't have to, like, start a server and talk to it.

05:25 I'd literally just, like, use the same API here but, like, directly skipping all the infrastructure.

05:31 Oh, that sounds fun.

05:32 Yeah.

05:32 In, like, you know, a nerdy kind of fun.

05:34 Yeah, that sounds, like, really fun.

05:36 But, yeah.

05:37 It is also fully type annotated.

05:41 So it's very, like, Python 3 friendly.

05:43 Has 100% test coverage.

05:45 And, yeah, it has support for mocking out things.

05:48 A lot of cool, nice features.

05:50 And the fact it's built, all this is built on top of requests means if you're already using requests, it's, like, you can adopt it pretty easily.

05:57 Because you don't have to change anything.

05:59 But if you want to bring in some of these cool new features, it's just, you know, you start using them.

06:03 Nice.

06:03 Now, one caveat.

06:04 I does say that this is in some form of alpha.

06:07 It's, like, pretty early days for this project.

06:10 But, yeah, it says this project should be considered an alpha release.

06:14 It's substantially API complete, but some areas need to work.

06:17 Well, you know, people start using it.

06:19 People start contributing to it.

06:20 All of a sudden, it becomes not alpha.

06:21 So, anyway, it's out there for people to use.

06:24 This is sort of the same umbrella area as Tom Christie in the Django REST framework area.

06:29 So, I think it's a pretty good place.

06:30 Cool.

06:30 Okay.

06:31 Neat.

06:32 Speaking of stuff under the encode GitHub project.

06:35 So, Starlette is part of that also?

06:37 Yeah, yeah.

06:38 Okay.

06:38 So, interesting that we did not plan this.

06:41 No.

06:41 The universe planned it to line it up for us.

06:44 Oh, my gosh.

06:45 We're wearing the same color shirt also.

06:47 Oh, my gosh.

06:48 It's black.

06:50 So, that's not that weird of a coincidence.

06:52 Anyway, so, the article I wanted to highlight was a quick and dirty mock services with Starlet.

06:58 This is by a friend of the show, Matt Lehman.

07:00 Hey, Matt.

07:01 It's kind of a neat thing.

07:03 So, one of the ideas, I don't know if we've talked about this on the show, but the idea of testing.

07:08 So, if you've got something that you're part of your application or the thing you want to test talks to a third-party service, sometimes you might not actually want to call that third-party service.

07:19 You can mock or stub out the calls to the service.

07:23 Or, one of the ideas is to create a fake service to talk to.

07:27 And so, that's what this article is about is how to create a fake service so that you can use it for testing.

07:34 And Matt writes a really quick one using Starlet.

07:38 And I think you could use other frameworks, too.

07:41 But I like how simple this is.

07:42 One of the needs he had was that he didn't want the service to, like, just come back right away.

07:48 He wanted some delay because there were some of the service calls that were taking up to a minute or longer.

07:53 And he wanted to be able to simulate that within his fake service so that you can test the code under test to make sure it is working correct with a big delay.

08:02 Anyway, like, it was a really easy-to-read write-up, easy way to do that.

08:06 And I know that that's a recommended practice for a lot of instances.

08:10 But there aren't any very many write-ups of how to do it.

08:14 And so, congrats, Matt.

08:15 I like it.

08:16 Yeah, that's really cool.

08:17 When you think of dependencies, you think of, well, my database layer or Stripe API or something.

08:24 But time is one of those dependencies that's really tricky.

08:27 Most of the time, one of the reasons why we try to do a Maka service is to eliminate that time.

08:32 You know, you want to make sure that the flow of the data goes quickly, but you don't want to wait for the service during your test.

08:38 But at the same time, you kind of want to make sure that you're – and in most test cases, maybe that's the right thing.

08:45 But you also want to make sure that your application deals with long latencies if that's the normal situation.

08:52 Yeah, for sure.

08:53 You might want to shrink it from five minutes to five seconds or something so the tests don't run super long.

08:58 But yeah.

08:59 Or even like I want to start something and then see in an hour if it's done.

09:04 You could like, you know, have to fake out like, you know, datetime.now.

09:07 So the next time you ask it, it's like two hours from now, but it's one millisecond, you know?

09:12 Yeah.

09:13 Well, and, you know, make sure your parameterization of that test doesn't make it so that you're running 50 different versions of the hour-long wait.

09:21 Yeah, for sure.

09:23 All right.

09:24 Now, before we get to the next item, let me tell you about some new services from DigitalOcean.

09:28 So they've traditionally had recently added Postgres as a service.

09:33 So now hosted database, which is really cool.

09:35 But maybe you don't use Postgres.

09:37 Maybe you want to use Redis.

09:38 We just talked about queuing and stuff.

09:41 So Redis is now a service that you can get over at DigitalOcean.

09:45 Redis as a service.

09:46 And also MySQL as a service.

09:48 So check that out over at pythonbytes.fm/DigitalOcean.

09:53 Get $50 credit for a new user.

09:54 They've got all sorts of good stuff coming your way.

09:57 And, of course, all of our infrastructure runs on it as well, which is fun and very good.

10:02 All right.

10:03 The next thing I want to talk about is that other larger cloud provider.

10:08 Have you heard of something?

10:09 Some people started using this, Brian.

10:11 It's called AWS.

10:12 Have you heard of this?

10:13 I think so, yeah.

10:14 Yeah.

10:14 I think like a quarter of the internet at least runs on it.

10:18 So the Python API, the Python package that used to talk to things like S3 and EC2 and AMI and all the stuff over at AWS is called Bodo.

10:30 Yeah.

10:31 So there's a project brought to our attention by Giuseppe Consolo.

10:35 Thank you for that, Giuseppe.

10:37 Called Mocking Out AWS APIs with Modo.

10:41 So if you're going to mock out Bodo, what would you call it?

10:44 Modo.

10:45 Oh, nice.

10:46 Now, it lets you do all sorts of stuff.

10:48 Like if you, say, want to mock out an S3 bucket, right?

10:54 So S3 is like file blob storage.

10:55 You can mock it out and then like save a file to it and then see if it's there.

11:00 Preload it with data and then interact with it.

11:02 And it's got all sorts of interesting stuff.

11:04 But if you start to dig through this library, it turns out it's got both some really interesting ways to test stuff and it's also super comprehensive.

11:13 So, for example, if I want to test working with S3, I can say from Modo import mock S3.

11:20 And then I just put an at mock S3 on my method, on my test method.

11:25 And now all of my true S3 calls with Bodo actually don't go to Bodo.

11:30 They go to this Modo mock version.

11:32 Oh, that's neat.

11:33 Yeah.

11:33 But apparently it doesn't really end there because if I wanted to work with like EC2, I could import mock EC2 and put that on there and say Bodo dot working with, you know, do all the Bodo client EC2, whatever.

11:45 It's like, I don't know why Bodo is so terribly complicated.

11:48 It's like super overloaded.

11:49 But anyway, that's a different time for a different story.

11:52 You can go and like ask for your reserved instances and it'll use your mocked out ones and things like that.

11:57 So, just like if you were to go flip through all the stuff that it supports, ACM, API gateway, auto scaling, cloud formation, cloud watch, cloud watch events, cognito identity, data pipeline, DynamoDB, et cetera, et cetera.

12:12 And we're only into the Ds, right?

12:14 It just goes on and on and on of all the different AWS things that it mocks out.

12:19 So, if you got to test AWS, this is pretty killer.

12:21 Like you can mock out Glacier, IoT, Kinesis, whatever, you name it.

12:26 One of the notes you added was that it can run as a standalone server mode so that you can even test non-Python code.

12:33 That's right.

12:34 That's right.

12:34 So, apparently I can, yeah, there's a way to run it as a server and then talk to it as if it were some of these services as well, which is pretty wild.

12:43 It is incredible.

12:44 And I guess good that Bodo is all of the interface so that it's one point to mock it.

12:53 Yeah, yeah, Bodo, the reason I don't like it is you say Bodo and you create a client and then you pass it a string.

12:58 The string could be like EC2 or S3.

13:00 And then how you interact with it has to do with what factory that string drove it to to create a different, like it's really hard to discover what can I do with this thing?

13:10 Because it's like a generic blob that can be converted into something more real.

13:15 I don't know.

13:16 It's funky.

13:17 But yes, in this regard, it's definitely very nice.

13:19 Apparently, your life is more complicated than mine.

13:21 I guess I've worked with S3 and Elastic Transcoder and all that stuff too much.

13:26 Yeah.

13:26 Okay, cool.

13:28 What's next?

13:28 I was just thinking that maybe we should have done a testing code episode with all this testing stuff.

13:32 Yeah.

13:33 But one of the things that we have, I think we've talked about Mongo Engine before.

13:37 And I know that you've used Mongo Engine in some of your courses, right?

13:42 Yeah, for sure.

13:42 And on the sites.

13:43 Yeah.

13:44 And it's like a mapper's...

13:46 They don't say ORM because object relational mapping because it's not really the R.

13:50 So they say ODM for object document mapper.

13:53 That's like the...

13:53 Okay.

13:54 The adapted acronym.

13:55 So I didn't know that there were...

13:57 So I knew about Mongo Engine because of you, but I didn't know about others.

14:00 And here's a project called MicroMongo or Mew Mongo.

14:04 Not sure.

14:05 Yeah.

14:05 I think MicroMongo is the way you would say it.

14:06 I think that's the Greek.

14:08 Yeah.

14:08 Mew, yeah.

14:09 It's a Python MongoDB ODM.

14:11 And it comes because the people that made it had two needs.

14:15 They wanted an asynchronous ODM.

14:18 And for some reason, they felt like it was difficult to do serialization and un-serialization of documents

14:26 with existing ODMs.

14:27 And so this one doesn't.

14:28 So it's an asynchronous sort of thing.

14:31 It works directly with some of the popular Mongo drivers like PyMongo, TX Mongo, that's

14:38 for twisted, Motor AsyncIO, and MongoMock.

14:41 And I'm kind of excited about MongoMock.

14:45 Yeah, for sure.

14:45 I got to say, this is probably the most interesting aspect of this because something like Mongo

14:50 Engine, it just works with PyMongo.

14:51 But this one says, you know what?

14:52 We actually can work with all these different foundations, which gives it like fundamentally

14:58 different behaviors and features, right?

15:00 If you pointed at the twisted one, it can now integrate with twisted's async-ers model.

15:04 Motor AsyncIO now lets it plug into true AsyncIO stuff.

15:10 Like you could plug it into Scarlet like you discussed before and those async methods there.

15:14 And then the mocking as well.

15:16 Like we're just going to take the foundation and make it a mock version.

15:19 All those are super cool.

15:20 Yeah.

15:21 And then also because of this, I learned about MongoMock, which is a cool way to mock out

15:26 your Mongo database.

15:27 So kind of neat.

15:29 Definitely want to try this.

15:30 Yeah.

15:31 Sometime.

15:31 Yeah.

15:31 It definitely looks interesting.

15:32 I don't remember exactly when this got created.

15:36 Do you know what GitHub needs?

15:37 Is it needs a, this project was created on this day, right?

15:40 Because you can go back to the commits.

15:42 But if there's thousands of commits, you've got to like infinite scroll your way back to

15:46 the origins.

15:48 But anyway, yeah, it's really cool.

15:50 I think if I had known about this at first, I might've even chosen this over MongoEngine.

15:54 But yeah, it's really nice.

15:55 It's nice.

15:56 It has a 90% plus test coverage.

15:59 I really like the different foundations, which means you learn like one ODM style of programming,

16:05 but then you can use it in these different situations.

16:07 Yeah.

16:08 Super cool.

16:08 I like it.

16:09 And also I didn't know about MongoMock either, but now I'm excited about it.

16:12 That looks cool.

16:13 Nice.

16:14 All right.

16:14 So let me hit you with an opinionated piece here and you can give me some feedback.

16:22 Let me know what you think about it.

16:23 Some parts of it.

16:25 Tomatoes ready.

16:26 Yeah.

16:26 Perfect.

16:26 Get ready to throw them.

16:27 I'll get ready to dodge them.

16:28 So this article was sent over by Tyler Madison.

16:33 Listener, thanks for sending that over.

16:35 Now it's called the single responsibility principle in Python.

16:39 And the single responsibility principle or SRP comes out of this larger group of design principles,

16:46 coding principles, architecture principles, something like that called solid.

16:50 So S is a single responsibility principle.

16:53 O is the open close principle.

16:55 L is Liskov substitution principle.

16:57 And these even come in really interesting demotivator style posters.

17:02 I don't know if you're all familiar with demotivators.

17:05 You're probably familiar with motivators.

17:07 You will see this probably in the entryway, like kind of near the CEO office.

17:12 It's like probably got a giant eagle soaring above clouds.

17:15 Like together we can fly higher than you ever dreamed or something stupid like that.

17:20 Well, the demotivators take pictures like that but put like negative captions and connotations.

17:26 So these are really interesting things.

17:28 For example, the Liskov substitution one has a duck and a rubber duck in the water.

17:33 And it says, if it looks like a duck and quacks like a duck but it needs batteries, you probably have the wrong abstraction.

17:38 Right?

17:39 So there's all these really cool demotivator style posters around the solid principles.

17:45 And I think the solid principles are pretty neat.

17:47 Like you can go overboard with them.

17:49 But they are pretty powerful.

17:50 So this article focuses specifically on the single responsibility principle.

17:54 And I think talking about it is really interesting.

17:58 I think the solution is unexpected.

18:02 And it made me think.

18:03 And at first I'm like, oh, I don't like it.

18:05 But after a while I'm like, but it is pretty creative.

18:08 And things that make me think I like to share with people.

18:10 So I guess that's why I chose it.

18:12 And it starts out by saying this article will guide you through the complex process of writing simple code.

18:17 And it talks about stuff that you would expect.

18:20 Right?

18:21 So there's some kind of super long function.

18:24 So this is terrible.

18:25 You can't test it.

18:26 You can't reason about it because it's doing five things.

18:28 How do you know whether you're doing too much?

18:31 You can look at measures like the cyclometric complexity, the Halstead complexity thing, the number of arguments, the number of statements, body length, all that kind of stuff.

18:40 And that will help you condense down into, say, reasonable classes or functions.

18:45 And this is really mostly focusing on functions.

18:47 So all right.

18:48 So let's think of another simple example.

18:50 I want to calculate the price of a bunch of products given a list of products.

18:54 And it seems all well and good except for that it also happens to use some kind of logger object and log out the price.

19:01 Well, now this thing is doing two things.

19:04 It's one, calculating it and logging it out.

19:06 What if you want to test it or work with it with its computation bits but not the logging bit or something like that?

19:12 So how do you go around dealing with these types of dependencies?

19:16 Right?

19:16 This kind of goes back to the testing side of things, Brian.

19:19 Yeah.

19:19 How do you deal with things like files and database connections and or database connection objects and, you know, loggers and whatnot?

19:29 So this guy takes us through this whole sort of thought process, which is really good, and says, you know what we're going to do?

19:37 We're going to come up with callable classes.

19:41 Oh, dear.

19:43 Well, the callable classes will be given the dependencies, and then you call them like functions, allowing you to pass different dependencies or other dependencies.

19:54 And now that seems like a really bad idea, but they're using a lot of – my first thought was, okay, well, that's, you know, that's like a curvy-shaped weird hammer that I guess you could hit it with.

20:07 But it's using a lot of interesting Python features.

20:10 So if you create a class that has – it's a data class.

20:15 You set frozen equal to true, slots equal to true.

20:18 It means all of its variables become internal and private only.

20:22 You set it to be private or final, rather, so it can't be derived from.

20:25 And you give it a call function.

20:27 It turns out to be pretty interesting.

20:29 And then they talk about some dependency injection frameworks that work with these types of classes.

20:36 In the end, it comes up with a pretty interesting way to, you know, create these things and then call them.

20:43 I'm not sure that I love it, but it definitely made me think, and I was a little bit surprised by it.

20:48 So I thought you all might enjoy it.

20:49 Yeah.

20:50 It's an entertaining read.

20:52 I'll give you that.

20:53 Yeah.

20:53 Look at the very last code fragment in that article, Brian.

20:57 Okay.

20:58 The last code fragment that says, like, send postcards equals container dot resolve.

21:06 Send to these postcards.

21:07 Yes, exactly.

21:07 And a little bit above it, yeah.

21:09 Yeah, yeah.

21:10 So it is powerful in that you can create these objects.

21:13 You can pass in mock objects without actually using mock dot patch and stuff.

21:18 It's interesting.

21:20 I don't think that I would go write software using this today, but I might use some of the concepts of it, maybe.

21:28 My opinion is that it is possible to do this, and it might make sure that you don't get fired because nobody else can edit your code.

21:37 Yeah.

21:37 Okay.

21:39 So I do like the single responsibility part of solid.

21:43 I am not a fan of the Luskov substitution principle or the open closed or dependency injection is something that frightens me.

21:55 These are things that were put in place to work around limitations of other languages like Java and C.

22:01 And I'm going to get tomatoes thrown at me for coming up with saying this, but some problems are complex.

22:07 And you can remove the complexity out of one part of your code, but it moves to somewhere else.

22:14 And maybe it makes your unit tests better, but your system tests are just the same.

22:19 Or it makes writing regular code super hard because you're like, where did this database connection come from?

22:25 I have no idea how I got here.

22:27 Right?

22:28 Yeah.

22:29 It is a thing.

22:30 It's an interesting article.

22:30 I'm not a fan.

22:32 Yeah.

22:32 I submit this to everyone as a thought piece, not necessarily as guidance.

22:36 Yeah.

22:37 But I'm not willing to say that it's wrong.

22:39 It's just my opinion.

22:40 All right.

22:40 Well, I think we should leave it there for our main topics.

22:43 Got anything you want to share with folks this week?

22:45 I've got one quick one.

22:46 Oh, you share it with us then.

22:47 So do you know Bob and Julian over at PyBytes?

22:51 Of course, right?

22:52 Well, they recently have their code challenges platform, which is at codechallenge.is, right?

23:00 The .es extension, right?

23:02 Just code challenges, put the period between G and ES.

23:05 And that's a place where you can go do practice exercises.

23:08 And it's a cool platform they've created.

23:10 And they just came out with Byte220, where they walk people through analyzing the Python Byte's RSS feed.

23:18 Oh, cool.

23:19 Yeah.

23:19 So you can go in there, and it shows you how to use Feed Parser, which is a really cool way to parse RSS feeds.

23:24 Go in there and actually answer questions.

23:27 What were they looking at?

23:29 It was things like, what is the average duration of an episode in seconds?

23:34 What are the number of episodes with special guests?

23:37 Like Trey was on the show last week, for example.

23:40 What are the most common domain names we mentioned?

23:42 Like GitHub probably is right up near the top and things like that.

23:45 So it's pretty cool that there's like this guided challenge to get through our platform, at least our platform's data.

23:51 Yeah, I'll have to come up with ways in our show notes to break their challenge.

23:55 No.

23:56 Yeah, that sounds good.

23:59 So that's all fun.

24:00 People can check that out if they're interested.

24:02 You ready for some jokes?

24:04 Definitely.

24:04 These come from Web Boss's Dad Jokes GitHub repo.

24:08 So they're pretty short and sweet, but they're in the dad joke style, which is kind of bad jokes that are hopefully funny.

24:15 So what do you get when you cross a computer and a lifeguard?

24:19 I don't know.

24:20 What?

24:20 A screensaver.

24:21 What do you get when you cross a computer with an elephant?

24:25 Lots of memory.

24:27 Lots of memory.

24:28 Lots of memory.

24:29 Okay.

24:31 Nice.

24:31 So I'm going to share this because I made it up and I thought it was funny, but it's an anti-joke.

24:37 So a Python developer, a PHP developer, a C# developer, and a Go developer all went to lunch together.

24:42 Oh my gosh, what happened?

24:43 They had a nice lunch and all got along fine.

24:45 Perfect.

24:46 Of course they should.

24:47 They're all developers.

24:49 Anyway, thanks.

24:52 Awesome.

24:52 Yeah, well, thanks as always, Brian.

24:54 See you later.

24:55 Thank you for listening to Python Bytes.

24:56 Follow the show on Twitter via at Python Bytes.

24:59 That's Python Bytes as in B-Y-T-E-S.

25:02 And get the full show notes at pythonbytes.fm.

25:05 If you have a news item you want featured, just visit pythonbytes.fm and send it our way.

25:09 We're always on the lookout for sharing something cool.

25:12 On behalf of myself and Brian Okken, this is Michael Kennedy.

25:15 Thank you for listening and sharing this podcast with your friends and colleagues.

Back to show page