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


Transcript #20: Finding similar but not identical images in 128 bits via Python

Return to episode page view on github
Recorded on Tuesday, Apr 4, 2017.

00:00 Hello and welcome to Python Bytes. This is episode 20 where we are delivering Python news and headlines directly to your earbuds.

00:08 I'm Michael Kennedy.

00:10 And I'm Brian Ockin.

00:11 And we've got a bunch of stuff lined up for you today. I'm really excited to share, especially this first article, which is so clever that you chose, Brian.

00:18 Before we do, I want to say thank you. Thank you to Rollbar, who's back to sponsor a bunch more Python Bytes.

00:25 And we'll talk more about Rollbar later, but thanks, Rollbar.

00:28 That's awesome.

00:28 Yep. So we were just talking about pictures. Like I have many gigabytes of pictures.

00:33 And if you ran a website that accepted uploads in large numbers of pictures, how do you deal with all that data?

00:41 Especially there's probably a lot of duplicate data, right?

00:43 I'm not sure. And so this is an interesting article. There's an article from jetsetter.com.

00:49 And they're an invitation-only travel community.

00:53 But the article is duplicate image detection with perceptual hashing in Python.

00:58 And that actually sounds more...

01:01 Perceptual hashing. That's awesome.

01:03 Perceptual hashing. It's awesome.

01:06 And the idea is they've got... I mean, the site's got a bunch of pictures of different places around the world.

01:12 And they don't want pictures that are mostly close to each other.

01:17 I mean, for family photos, you got a ton that are close to each other.

01:20 But I get for like...

01:21 There's a lot of cases where you don't want things that are almost the same.

01:24 Right. Like pictures of hotels or pictures of a marina to say, here's the view out of the hotel.

01:31 Like if they're going to have a listing on like some location of some hotel and they ask people to upload them, they don't need like 100 ones from this one view.

01:38 And if you check out jetsetter.com, it is an intensely photo-heavy site.

01:43 Like I'm pretty impressed with the number of photos on that page.

01:46 With the idea of perceptual hashing, I was definitely interested in reading about this.

01:50 And I expected it to be a fairly complicated algorithm.

01:54 But it's actually ingenious.

01:56 And it's a...

01:57 They use Python and get...

02:00 Transfer the image down to just a 9x9 square.

02:03 I don't get...

02:04 Of gray values even.

02:05 I don't get how that's enough information.

02:07 But it is apparently enough to determine whether or not an image is close to another image.

02:15 And they do a delta.

02:16 I'm not going to be able to...

02:18 Can you explain that much better?

02:19 I can try.

02:20 I mean, when I read, we take a 5 megapixel image and we generate a 128-bit hash.

02:28 And that means a thing.

02:30 Like that means uniqueness.

02:31 Or actually it means similarity, which is actually more important.

02:34 I was like, okay, I have to figure this out.

02:36 And I guess what they do is they take a large image and they like average it down to a 9x9.

02:42 They say for larger images, like a 17x17 image.

02:46 And to determine the similarity, maybe somebody's off by 5 feet to one side or the other to take a picture of a hotel or a view or something.

02:54 But if you kind of average it down to that 9x9, that's where the similarities kind of collapse into those grids.

03:01 And then they run an algorithm on that grayscale grid, right?

03:04 Yeah.

03:05 And then the interesting thing is that, of course, it's clear to me that you could come up with a hash algorithm for an image.

03:12 But the difference in the hashes is enough to tell you how close the image is.

03:19 Yeah.

03:19 And it's actually the opposite that really blows me away is like two similar images that are not the same generate the same hash.

03:27 That's what's the magic.

03:28 Like that totally blows my mind.

03:29 I could see like, well, obviously hash is different, images are different, but images are similar, not the same, hash the same.

03:35 That blows me away.

03:36 Yeah.

03:37 And I like it that it's not that complicated of an algorithm and it's a fun read.

03:42 Yeah.

03:42 That's, you know, so I think there's a couple levels of interesting that you brought up this article.

03:47 And one of them I think is really interesting is when I first heard that, I thought, okay, one, this is going to be super hard, super computational.

03:53 Two, maybe this is like machine learning or something like that.

03:58 Like two machines, like two images given to an AI, like a deep learning neural network or something.

04:02 You say, yeah, these are sufficiently similar in ways that I don't really, people don't really understand.

04:07 But magic on GPUs and lots of, you know, neurons, it works out somehow.

04:11 But the fact that it's really, really a simple algorithm is what's, what's I think kind of special about it.

04:18 Right.

04:18 It's like, hey, there's still lots of places to be clever and not just throw AI plus GPUs at a thing.

04:24 Yes, definitely.

04:25 Yeah.

04:26 And not only that, you get to take it with you, right?

04:29 It's available on GitHub.

04:30 Yeah, they do have it.

04:31 It's a, what is it?

04:32 P-Y-B-K-Tree?

04:34 Py-B-K-Tree, whatever that means.

04:37 Okay, awesome.

04:37 I'm sure it's part of the algorithm.

04:39 Excellent.

04:40 So keeping with open source projects that you can go find and just grab and do cool things with,

04:45 one of the listeners pointed me towards, pointed us towards Google open source.

04:50 In fact, it was the guy from Google Fire, Python Fire, which we'll talk more about later.

04:56 But he has one of the projects there.

04:58 And on Google open source, they've basically created like a listing directory of all of the open source projects.

05:05 Now, many of the projects still live on GitHub.

05:07 But this is like a place where you can go search and analyze and discover projects from Google.

05:13 And what's cool is you can sort by language.

05:15 So show me the Python projects.

05:17 Show me the C++ projects, whatever.

05:18 So I grabbed six or seven interesting projects.

05:21 I just wanted to run them down for you, Brian.

05:23 Okay.

05:24 Yeah.

05:24 So one of them is subprocess 32, a reliable subprocess module for Python 2.

05:29 Apparently, subprocess built in is not reliable for Python 2.

05:34 I don't know.

05:35 But I didn't know that either.

05:37 That's partly why it's interesting to me.

05:39 But also, you know, there it is.

05:40 That's cool.

05:41 Grumpy.

05:42 We've talked about grumpy before.

05:43 Grumpy is Python on Go instead of Python on CPython.

05:46 Yeah.

05:47 Yeah.

05:47 That's a good one.

05:47 That's a...

05:48 Python Fire, of course.

05:49 Python Fire, of course.

05:50 Like I pointed out, that's a way to take any Python object or module and turn it into a command

05:55 line interface.

05:56 There's a Python client for Google Maps services.

05:59 So if you want to consume Google Maps from Python, do it.

06:03 There's Hue, H-Y-O-U, a Python interface for manipulating Google spreadsheets.

06:10 That's cool, right?

06:11 Okay.

06:11 I'm going to have to try that out.

06:13 That's neat.

06:13 Yeah.

06:13 I mean, I've seen the stuff for working with Doc.

06:16 XLS, X files, the Microsoft Office ones, but I didn't know about the Google spreadsheet.

06:21 So this is cool.

06:21 Another thing that's always tricky for me is working with OAuth, right?

06:25 There's always this, like, I've got some app.

06:27 The app needs to go, like, open a browser window, and there's some sort of funky callback, and

06:31 things happen.

06:32 And so one of the places that's especially challenging, I think, is over a command line interface.

06:38 Well, there's OAuth 2L.

06:41 I think it's L.

06:43 And what that is, is it's a way, a command line tool to get an OAuth token.

06:47 Just let that sink in for you.

06:49 Okay.

06:50 So I want to log in as Google.

06:51 I can do that, like, through my app.

06:53 Like, I could basically create a shell script that, through the CLI, gets an OAuth token from

06:59 the user.

06:59 That's pretty interesting.

07:01 Okay.

07:01 And also, I talked about the Google Maps API.

07:04 Like, that sounds like that's something that's really hard to, like, unit test or test at

07:08 all without actually going to Google.

07:10 So there's a mock maps API.

07:12 So a small little app engine app for testing, like, basically mocking out Google Maps API.

07:18 And last but not least, TensorFlow.

07:21 The amazing deep learning, machine learning stuff.

07:24 That's about 50% Python, 50% C++, and a lot of GPUs in action there.

07:31 And I don't know where I read this, but I think that this Google open source location is not

07:37 just all projects.

07:39 It's projects that they consider still active.

07:41 Okay.

07:42 Yeah, that's cool.

07:43 I mean, obviously, you don't want just, like, a dumping ground, right?

07:45 Yeah, cool.

07:47 I mean, everything in there looked pretty neat and fresh, so it's good.

07:49 It's a fairly neat interface, too, with, I guess, panels and stuff.

07:54 Yeah, it's worth checking out.

07:55 Okay.

07:55 What do we got next?

07:56 Oh, next is me.

07:57 Yeah, more machine learning type stuff.

08:00 Yeah.

08:00 So there's an article from Jason Brownlee called, and I just clicked away, How to Handle Missing

08:07 Data with Python.

08:08 And this is something that I definitely deal with measurement values that deal with at work,

08:13 but there's, the gist of it is, is a lot of times you're dealing with a lot, large or small

08:18 data sets, and some of the values are missing.

08:21 And there's a whole bunch of different ways you can deal with missing data, but there are

08:26 a few of them that he talks about are replacing, you know, you have to know what the magic number

08:31 is that some data collection will fill in a zero, maybe, if there's no data, or some other

08:36 known number.

08:38 But all your math is going to get messed up if you actually just leave that there.

08:43 So there's a couple ways to get around it.

08:46 One of the ways he lists is using magic, not a number values.

08:50 And I think pandas can deal with that correctly and not average those in.

08:55 Yeah, what I think is really nice about it is like, I could be given a CSV file or some

09:00 sort of data thing, set of data, and I could like work my way through it and maybe find

09:06 the bad data and fill it in potentially.

09:08 But his fix are like, you run this one line in pandas and magic happens and it's better,

09:14 right?

09:14 It's like the fix is so much better than the fixes that I would come up with.

09:17 Yeah.

09:18 And I do like that he's talking about different ways to deal with it with numpy, even without

09:23 pandas also, because you might not be using pandas.

09:26 But the, like one of the ways you would do it with any math package really would be to,

09:31 oh, I guess I don't know how to do that.

09:33 Actually.

09:34 Nevermind.

09:35 Filling in the, you'd somehow have to find all of the values anyway and fill them in with,

09:41 like one of the ways is if you're, if you're calculating an average, calculate the average

09:45 of everything else and then fill in the blanks with the average.

09:49 Right.

09:50 I guess it depends on what you're going to do.

09:51 Are you going to average it?

09:52 Are you going to max it in a minute?

09:53 You could like push that through, right?

09:55 Yeah.

09:55 Yeah.

09:55 Interesting.

09:56 The best solution definitely, I think is, is using the not a number and letting the,

10:01 the libraries take care of it for you.

10:03 But it's definitely, I wanted to bring this up partly because anybody that's working with

10:08 data collection or, and doing math with that has to deal with the fact that sometimes there's

10:14 not numbers there and you have to deal with it.

10:16 So.

10:16 Okay.

10:16 Awesome.

10:17 He's from machine learning mastery.com.

10:20 I think.

10:20 And he's got just a ton of cool stuff going on over there.

10:25 Right.

10:25 It's not just this one article.

10:27 Right.

10:27 So if you're into these kinds of things, definitely check it out.

10:29 Yeah.

10:30 It looks good.

10:30 Okay.

10:32 So what's up next is the hug rest framework.

10:37 But before we get to them, I want to give roll bar a hug.

10:39 Roll bar is awesome.

10:41 I've been, as people know, I've been using them for a long time on the websites and the

10:45 websites are getting more and more traffic.

10:46 And I recently, I'm not sure whether it was a wise decision or not, because I'm really busy

10:52 with other stuff, but I just got really frustrated with the way my servers are working, the way

10:57 I could sort of move them around and performance and stuff.

11:00 So I said, that's it.

11:01 One day I just woke up, so that's it.

11:03 Converting it all to MongoDB.

11:05 And so that was last week.

11:07 And that took like three days of rewriting all my sites to Mongo, which I really think

11:12 Mongo is the right choice.

11:13 And I'm just loving the way it's working now.

11:16 But that was a pretty serious, like take the guts out of all my web apps and stick in a new set

11:22 of guts that are similar, but not entirely compatible.

11:25 I spent a little time with roll bar and they, they, they helped me out and find a few

11:30 problems like where maybe types used to be strings.

11:33 I compare them where one was no longer a string and they didn't compare the same.

11:37 So I got weird errors, but roll bar made it super easy to track that down.

11:40 So if you want to have reliability and most importantly, awareness of the state of your

11:47 apps, plug in roll bar to your web apps.

11:49 You can use it in pyramid, flash, Django, whatever, just plug it in and you'll get notifications

11:55 right away.

11:56 So be sure to visit rollbar.com slash Python bytes, and you'll get a special offer to get

12:01 started there.

12:02 And I bet that you definitely noticed those messages, but I didn't even notice you were

12:07 mucking with things.

12:08 And I'm pretty sure that nobody else did or very few people did either.

12:11 Yeah, that's true.

12:12 And thank you for saying that.

12:13 But I actually know how many people ran into problems, right?

12:17 There was a couple, but I got an email from a couple of people saying, Hey, I had this problem

12:22 with your app.

12:23 I'm like, I know, but I didn't know your email address.

12:26 But I know what your problem was, and it's already fixed.

12:28 I just couldn't contact them.

12:30 So because they hadn't actually created an account yet.

12:32 So it was really nice to be able to just say, yeah, actually, the problem you're telling me

12:36 is already fixed.

12:36 I just couldn't communicate that back to you.

12:38 Really sorry about that.

12:39 It's awesome.

12:40 You seem like a big team then because of that.

12:42 So oh, yeah, definitely.

12:44 It's all the folks here in the cubicle farm.

12:46 We're busy.

12:47 You know, one of the next things that I want to do is build some some nice APIs.

12:54 And I think it's really an interesting time for the web in Python.

12:58 There's a lot of flowers blooming, if you will.

13:00 Right?

13:01 We've got Pyramid, Django, Flask.

13:03 Those guys are all doing super stuff.

13:06 And like most of my stuff is Pyramid.

13:07 But we've got Jopronto coming along, Sanic.

13:10 And another one that I just learned about is called Hug at Hug.rest.

13:15 How's that for a name and a domain?

13:17 Yeah, actually, it is.

13:19 It's www.hug.rest.

13:20 Hug.rest.

13:21 That's beautiful.

13:22 So Hug is a Python framework, web framework, just specifically for building restful, documented,

13:30 documentable, versionable APIs.

13:33 And it's built both for like super simplicity and flexibility as well as performance.

13:39 So I started looking this up.

13:41 Wow, this is quite interesting.

13:42 Okay.

13:43 So the idea is you can create an API once and you can consume it in all these different ways.

13:49 So you can import it as a module or a package into your project and use the API that way.

13:54 You can communicate it, obviously, over HTTP as like a RESTful API.

13:59 Or it also has a CLI, command line interface, way to expose that.

14:03 So if you write like some kind of a web app or functionality you want to expose over an API,

14:07 but you also want to call it locally, it's like the same code.

14:11 Oh, wow.

14:12 That's interesting.

14:12 It's also written in Python 3.

14:14 It uses Cython all over the place.

14:17 So it's like super fast.

14:20 It's one of the fastest web frameworks out there for these kinds of things.

14:23 At least the non-async version, let's say.

14:27 If you compare those, it's pretty cool.

14:28 It's got a decorator model, so the code looks really clean.

14:31 Yeah, and the decorator model is cool because the decorator model will do like version management.

14:35 You can have like version 1 and version 2 of the API that have like different data formats.

14:40 And they can just coexist.

14:41 You get automatic documentation based on that.

14:45 Like it'll do type annotations and then like use the type annotations as part of the documentation and things like that.

14:50 Oh, that's great.

14:51 It's a pretty cool, simple little framework.

14:53 So, you know, hug for those guys.

14:55 Nice job.

14:55 Definitely.

14:56 Speaking of CLIs.

14:57 Yeah, speaking of CLIs, I'm actually working on, I had an example I wanted to do that I'm running with the pytest book that I'm working on.

15:06 And for the front end of it, I was punting before and not using actually putting a front end on the application.

15:13 But I wanted to at least put a command line interface in.

15:16 And my first attempt was to go down arg parse.

15:19 And the particular quirks of this application, I needed sub commands.

15:24 Actually, just the tutorials I found were out of date.

15:28 It doesn't work.

15:29 And I was having a little bit of difficulty.

15:32 So I went ahead and tried CLIQ.

15:34 I'd heard of CLIQ before and hadn't tried it.

15:37 And, man, a tutorial from like three years ago was about what I needed.

15:42 And it works right away.

15:45 I've got like half a page of code and my interface, my command line interface is done.

15:50 That's really cool.

15:50 That's also decorator heavy, right?

15:52 Yeah, in my sublime editor, it's colored nicely.

15:55 And my wife walked by and said, that's such beautiful code.

15:58 Oh, lovely.

16:00 Let's take that on many, many levels, right?

16:02 That's awesome.

16:03 Yeah, that's by Armin Roeneker, a guy from Flask.

16:05 So definitely.

16:06 Oh, did he do CLIQ?

16:08 I think so, yeah.

16:08 I believe so.

16:09 Yeah, nice.

16:11 CLIQ is cool.

16:11 I've done a little bit of work with it.

16:13 And I've liked what I've seen.

16:14 But I also kind of want to, yeah, we'll talk about it later.

16:17 But I might want to try adding a different CLI interface to it as well.

16:21 Yeah, cool.

16:22 So the last one that I chose for us is kind of a refresher, back to the fundamentals type

16:27 thing.

16:27 So Python inheritance class and instance class and static methods demystified.

16:34 So this one is on realpython.com.

16:37 And I went over there and checked it out.

16:38 And I said, oh, OK, realpython.com.

16:40 That's cool.

16:41 And then I realized this is actually from Dan Bader.

16:42 And we seem to be covering a lot of Dan's stuff over here.

16:45 And I actually have more to say about Dan later still.

16:47 So this was a guest pose Dan did for that, although I didn't realize that until I started

16:52 getting into it.

16:52 And the idea was to demystify what's behind class methods, static methods, and regular

16:58 instance methods.

16:59 If you learn Python classes, if you learn classes and inheritance and object-oriented programming

17:05 only through Python, this will be obvious to you.

17:09 But if you come from other languages like C++ or Java or C# or JavaScript, there's differences

17:16 to the way Python classes and inheritance works.

17:19 And it's worth kind of a compare and contrast.

17:22 So he comes up with a class.

17:24 And it's got like a regular method, a class method.

17:26 So an at class method decorator.

17:28 And takes a CLS parameter.

17:30 And a static method with an at static method decorator.

17:33 Nothing.

17:34 And basically compares and contrasts how they work.

17:36 And so some of the things that I think are not obvious when you're first getting started

17:41 is like instance classes.

17:42 Those are pretty straightforward.

17:43 Like you call them on instances like all other languages.

17:47 But the fact that I can call static methods or class methods on instances, that's a little

17:54 bit funky.

17:55 Right?

17:56 Yeah.

17:56 That seems a little weird.

17:57 And then the other one, the main one I think is like what's the difference?

18:00 Why are there two things like static method and class method?

18:02 They seem the same.

18:03 Why are there two?

18:04 And then like when would I use one versus the other?

18:07 Right?

18:08 The class method takes a CLS method, which is literally the type that it's on.

18:12 And the static method just doesn't.

18:14 But other than that, they seem the same.

18:15 Right?

18:16 And so if you're going to say like interact with the class, like during the class method,

18:21 if you're going to create an instance of the class, you can use the CLS parameter to support

18:27 like inheritance and stuff.

18:28 So if I got like a, let's say a vehicle class and a car, like a Tesla car class, that static

18:36 method could say like allocate a CLS, whatever that is.

18:39 And if you called it on a Tesla static ish function class method, it would actually create

18:44 a Tesla.

18:44 It would change like the thing, the type that it knows it is, where the static method is

18:48 just like a grouping.

18:49 So I thought that was interesting.

18:51 Does the class method follow then the hierarchy then?

18:54 So if I declare a class method on a base class, does it, is it available to the subclass?

19:01 Yes, always.

19:02 And that's always true for static methods.

19:03 But the difference is the static method doesn't really know what type it's being called on.

19:08 Oh, okay.

19:09 Whereas the class method, it's given the type.

19:12 So if there's like, you call it farther down in the inheritance chain, that whatever level

19:16 you're at, that instant or that type actually is communicated to it.

19:20 And so you're kind of, you're told where you are in the hierarchy in a class method, where

19:24 in static, it's just like, it's just a method.

19:26 Go for it.

19:27 Okay.

19:27 Yeah.

19:27 I don't think I've ever used static methods for anything.

19:29 Yeah.

19:29 Well, they're out there hanging out with their friend class methods.

19:32 Interesting.

19:34 Indeed.

19:35 So I have a quick follow-up from the last show.

19:37 David Bieber from Google, the guy who works on Python Fire, sent us a note.

19:41 And you said something to the effect of, look, Python Fire is awesome, but IPython is a serious

19:48 dependency to take if I just want to see a lie, right?

19:51 And I think that's fair.

19:52 That's fair.

19:52 But he said, hey, you know what?

19:55 One of our primary plans is to remove IPython as a dependency.

19:59 We're just not there yet.

20:00 So if anybody in the audience wants to help those guys move forward, they're totally working

20:05 on that.

20:05 And so Python Fire from Google is definitely getting some interesting thinning out, and

20:13 it'll be very nice.

20:13 And actually, I like to hear that, that they're working on eventually getting rid of that dependency.

20:19 And it's pretty cool.

20:21 Also, it's something I had mentioned when we talked about Python Fire, that your development

20:27 time is important, too.

20:28 And putting an interface together with that is pretty fast.

20:32 So keep that in mind.

20:33 Yeah.

20:34 It's not always about optimizing for the machines.

20:36 Definitely.

20:36 Hey, one more follow-up is we did cover PDR2 or PDR a couple episodes ago with the DUR colors

20:47 prints out.

20:48 One of the complaints I had was that it didn't look that great on my black terminal.

20:54 I had the same problem.

20:55 I like darker stuff.

20:56 And I'm like, wait, where's all the words?

20:58 They just updated it.

21:00 And I guess yesterday, I think.

21:02 And it does have color configuration now.

21:05 So you can drop a PDR2 config file in your home directory.

21:11 And I set my background color to magenta so that it was visible for docs, visible on both

21:18 black and white.

21:18 And now it looks great.

21:19 Oh, nice.

21:19 PDR2 now has themes.

21:21 Love it.

21:22 All right.

21:24 How's the book coming?

21:26 I heard there's a spotting.

21:27 Yeah.

21:28 So on Twitter the other day, somebody, a guy named Jacob Jarros, I think that's right,

21:34 noticed that it was listed on the Pragmatic Publishers website.

21:38 So it's out there.

21:40 That's awesome.

21:40 I love the cover.

21:41 The rocket is cool.

21:43 Yeah.

21:43 A 50s sci-fi nerd.

21:44 So yeah.

21:45 And just the perfect, it's perfect.

21:47 Like it's 50s, 60s vintage rocket.

21:51 So how about you?

21:51 Well, it has been a super busy couple of weeks.

21:55 I've been working on a couple of classes.

21:57 One of them I'm about to release.

22:00 By the time this recording comes out, it will be out.

22:02 So tomorrow, basically.

22:04 A course called Using and Mastering Cookie Cutter.

22:07 So really deep dive into what is cookie cutter?

22:10 How do you create and manage projects with cookie cutter?

22:13 I think it's going to be a really fun course.

22:15 And I also just a few hours ago launched Managing Python Dependencies with pip in Virtual Environments,

22:22 which Dan Bader, speaking of Dan Bader, came over to join me to write a class for us over here.

22:28 And we're shipping that as well.

22:30 So I took that course and I actually learned quite a bit from it.

22:34 It's not just like pip install done.

22:36 It's what is the process that you use to manage your dependencies?

22:41 How do you like, what is the thinking and workflow you use to evaluate whether a package is worth taking a dependency on?

22:48 And all sorts of cool stuff like that.

22:49 Bunch of best practices.

22:50 Launched both of those.

22:51 And I just started selling course bundles on Talk Python training as well to sort of go along with those.

22:57 So lots of stuff.

22:58 That's pretty exciting.

22:59 I got to check out the cookie cutter thing.

23:01 Yeah, thanks, Ed.

23:02 It'll be out tomorrow morning.

23:03 For everyone listening, that's today.

23:04 That's today.

23:05 But for you, Brian, that's tomorrow morning.

23:08 The magic of time travel.

23:10 Thanks so much for finding all these great items.

23:13 That was fun as always, Brian.

23:15 It was fun for me too.

23:16 And thanks to everybody for all your feedback that you send in.

23:19 Yep.

23:19 Thanks, everyone.

23:20 And thank you, Rollbar, for supporting the show.

23:23 Thank you for listening to Python Bytes.

23:25 Follow the show on Twitter via at Python Bytes.

23:27 That's Python Bytes as in B-Y-T-E-S.

23:30 And get the full show notes at pythonbytes.fm.

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

23:38 We're always on the lookout for sharing something cool.

23:41 On behalf of myself and Brian Okken, this is Michael Kennedy.

23:44 Thank you for listening and sharing this podcast with your friends and colleagues.

Back to show page