Transcript #194: Events and callbacks in the Python language!
Return to episode page view on github00:00 Hello and welcome to Python Bytes where we deliver Python news and headlines directly to your earbuds.
00:05 This is episode 194 recorded August 5th, 2020. I'm Brian Okken.
00:11 And I'm Michael Kennedy.
00:12 And this episode is brought to you by us and we'll tell you more about what we're shilling later in the day. I want to talk to you about mutants.
00:21 Mutants? Like mutant ninja turtle type things or what are we looking at here?
00:26 Sure, mutant Ninja Turtles. No, so mutation testing. So I really kind of, I think, in warming to mutation testing and it's kind of a neat thing. And I think we've covered it before, but this article is from Mosh Sadka and it's called an introduction to mutation testing in Python. There are a few, a handful of, I think there's like two or three different mutation testing libraries. Mutmut is one of them and that's what this article uses. And so if people are not familiar with mutation testing, here's the problem. So you can use code coverage tools like coverage.py to show how much of your code your tests are covering, but even if you get to 100% coverage it doesn't mean that you're really testing everything. And so mutation testing, what it does is it takes your code under test and it does some modification. So it modifies portions of your source code to simulate potential bugs. Like for example, it'll replace like greater than comparison with greater than equal or placing it with those sorts of edge cases and stuff are often where we muck up. If there's no boundary test around the boundary condition, you know, there'll be a problem. So every little change is considered a mutant and it generates all these different mutants and it does it in a fairly smart way. It can test your code fairly quickly with not too many mutants. So and then it runs your test suite on the mutant and the idea is your test suite should kill all of the mutants.
01:54 So in this article, he shows an example of three methods and one test case and 100% code coverage.
02:02 But he runs mutmut and 16 of them survive, and then talks about how to fix that.
02:08 So it's really good quick article.
02:11 Yeah, this is interesting.
02:12 And I like the emoji legend use for the output.
02:17 Yeah, it's a cute library.
02:19 Yeah, it is. You know, one thing that I don't understand about mutation testing is like I understand, okay, well, we're gonna change like a value of a variable or like the way if we're doing a test to make it it was less than we're gonna make it greater than and see if your test still pass and like those kind of things that totally seems reasonable. But if it goes in, I don't know if it does, maybe, you know, if it goes in, like, says, Well, you're doing a print statement. So we changed part of the print string, like, who's testing for that, right? Like, that would seems like it would survive.
02:49 Yeah, I'm not sure.
02:50 So it seems like there's certain things like I would just never care to test for the output of the print statement where the static string changes.
02:58 Like to me that just is not something I care to test, right?
03:01 But I feel like the sort of general case of mutation testing, you go, well, here's a piece of variable that I need to change around. Let's change a string and see if the test still passed.
03:11 Maybe it's just inappropriate for those types of scenarios. Maybe you only test stuff like at a lower level where you don't have a bunch of print statements.
03:18 But you've got logging and all kinds of things.
03:20 So I don't know.
03:21 But still, I do like the idea.
03:23 I think MUTMUT and some of the others have ways to specify which kinds of mutants to generate.
03:30 So I don't know if it does the print statement sort of example.
03:33 But I'm sure that there's ways to say, yeah, I don't really care about--
03:37 don't modify string values, for instance.
03:39 Yeah.
03:40 Yeah.
03:42 Don't modify constants or something, maybe.
03:44 Who knows?
03:44 Yeah.
03:45 Cool.
03:46 - All right, well, you know, that looks really interesting and Masha does a great job writing up these types of things.
03:51 We feature him a lot, very cool.
03:53 Next up, I wanna talk about asynchronous programming.
03:57 - Oh, nice.
03:57 - Yeah, so we, maybe we've covered this before.
03:59 Now, we've covered this a lot, but I don't believe we've covered Async Queue.
04:04 - I don't think so.
04:05 - I don't think so either.
04:05 So this is from Quora and it is not brand new.
04:10 Like, so I just wanna be really upfront, like this has been around since 2016, but it's pretty interesting.
04:16 And the idea is so much of what asynchronous programming, especially asyncio type of async and await programming is about is scaling while you're waiting, scaling the latencies, right?
04:29 So, you know, like I'm gonna call the Stripe service and it's gonna take, you know, half a second to return.
04:36 And so I want my web server to be able to go and just do stuff, you know, other requests instead of waiting for half a second while we're checking out some person or whatever, right?
04:45 But they've got a different use case.
04:46 What they're doing is they're running, I don't know if I said this is from Quora, they're running Quora.com, which is a really cool Q&A site.
04:54 I actually think Quora does a great job of having like solid, thoughtful answers.
04:59 Not always right, but thoughtful at least, which is pretty cool.
05:02 But what they do is they don't talk directly to their database because that would be too slow.
05:08 Don't get me started on that.
05:10 But what they're doing is they're talking to memcached or which, you know, or Redis or whatever, but they're using memcached to store a bunch of pre-computed query results.
05:21 So they don't have to keep going back to the database.
05:23 Like for example, when you go view a question, they want to see the names of the people who upvoted the question, right?
05:31 So it's kind of a complicated query, right?
05:33 I need to go, here's the IDs, maybe we store the IDs of the upvoter, then we're going to do a query, to join over on the user table and get their names back.
05:42 And then we're gonna show it, like that sounds expensive for lots of data.
05:45 So what they do is they basically store those answers, like this user goes to this thing in memcached.
05:52 But a lot of the latency around this has to do actually with the network call.
05:56 Like it's pretty close.
05:58 It's like, you know, one millisecond or something, but they've got to go get those names over and over, right?
06:03 'Cause the way that you store stuff in memcached is this ID has this name.
06:08 and you've got 50 upvoters, it's like, give me the name of this person, give me the name of that person.
06:11 So there's a way to do like a batch get, like here's all these IDs, go get me all the associated names.
06:18 And they've got like this dependency tree of these sorts of questions they have to answer.
06:23 So what they've done is they've come up with this thing called Async Queue, and it's all about batching asynchronous requests and converting them from a bunch of individual calls into like one massive call.
06:33 So they can like do what looks like asynchronous programming, say, go get me all these things.
06:38 And instead of doing a bunch of individual async and await type calls, the system looks at that and goes, okay, what that means is, turn that into one giant query where it's like all of these IDs go to all those things and then return them back.
06:51 - Oh, that's neat.
06:51 - Yeah, it's pretty neat.
06:52 So it's basically this way to write code that will take what would be a bunch of small independent requests and turn it into like a one-shot request for talking to things like caching servers and whatnot.
07:05 - Yeah.
07:05 Apparently this is like a core component of core's architecture.
07:09 And yeah, it's all about batching up these calls.
07:13 I didn't know core was Python on the backend.
07:15 Oh yeah.
07:16 Yeah.
07:16 They've got a really interesting Python blog where they can engineering blog, where they talk about all sorts of stuff.
07:21 So this was like written up on their engineering blog about sort of how they went from what they were doing before this, which was, you'd have to like write several functions that would prepare the things and then you could ask for it because they would be cashed locally and all sorts of funky stuff.
07:35 So there's a great write up on sort of the whole use case of this.
07:39 So this, like I said, is from 2016.
07:41 So it predates async and await.
07:44 And so they use the yield keyword, which is a sort of a more foundational way to get to break up functions into parts that run.
07:53 So basically you decorate a function and then you yield out the various steps.
07:57 And then before it executes all those, it looks at them, figures out what it has to do, and then it like batches it up and then does it all at once.
08:04 - Yeah, neat.
08:05 - Yeah, so I thought this was kind of interesting.
08:08 I think it's a little bit, just looking at the patterns here, it feels like it's a little tiny bit limited because it's targeted at, I believe they're still at least then, right?
08:20 So when they came up with it and it's still active, I think they built it for running a Python 2, right?
08:26 Remember this 2016, they've been running for a while.
08:29 So some of their APIs, I don't think, Like for example, they don't use the async and await keyword, I think because that didn't exist.
08:38 Like they supported Python 3.4 where async I/O was, but async and await didn't come along until just a tiny bit later, I don't think.
08:45 So anyway, a bit of a grain of salt, but I think this will be a pretty interesting thing that people can adopt and use for these types of scenarios.
08:54 Certainly if it powers Quora, it's probably pretty good.
08:57 - Yeah, neat, cool.
08:58 - Absolutely, yep.
09:00 - Another thing that's cool is Talk Python Training.
09:02 - Thank you.
09:03 We got a lot of stuff going on over there.
09:04 Actually, we've got a ton of new courses coming.
09:07 Course for people who generally live in Excel and should be adopting the Python data science tools.
09:11 So that's coming really soon.
09:13 We've got a getting started with data science.
09:15 I just actually, last time we spoke, I said, hey, I'm writing this course.
09:18 I started writing a course called Python Memory Management.
09:22 I finished it, I recorded it.
09:23 It's like a five-hour course.
09:24 It's gonna be awesome.
09:25 So now I'm on to writing a new course, Python Design Patterns.
09:28 - Oh, nice.
09:29 So that'll be out in a few weeks as well.
09:31 How about you?
09:32 Yeah.
09:33 I just wanted to highlight again, I have the URL, pytestbook.com set up to go directly to errata because I get a lot of people asking, "Hey, your pytestBook, is it still good for it?
09:47 It was like in 2017, still valid?" Yes, it's still valid, but there's a few gotchas and I list them out, very easy to read at pytestbook.com, it directs you to an errata page to show you, it's just a couple tweaks to the source code you got to make. You got to pin tinydb and a couple other things, and we'll try to get those changes out to the download link on Pragmatic as soon as possible. That's still in the works, but there's also a link to, if you have any issues, there's a link to the official Pragmatic errata page where you can ask questions, and if you have any, run into anything, I'd love to hear about it. And I'm excited to get a lot the lately a lot of the people that have been contacting me said they're excited about reading the book are machine learning people. So it's kind of neat to see data science and machine learning people add testing to their workflows. That's exciting. Absolutely. So I have a final call to action for people out there. If if you want to make sure that we have the time and energy to keep creating stuff like this podcast and the other things we're doing, you don't necessarily have to get our stuff but how about recommending it, right?
10:52 If your company needs to get up to speed on Python, recommend that your company buy the courses for that team, or if a company is doing a bunch of testing, have everyone on that team or the engineering group get Brian's book.
11:07 That would be great.
11:08 - Yeah, and then individually too, remind people that we do have a Patreon campaign going, so people can contribute a buck or two a month.
11:16 That would be great.
11:17 - Yeah, now that we go anywhere, we don't buy coffee.
11:20 Yeah. Next I want to talk, this sort of ties into your async thing.
11:24 Yeah, for sure.
11:26 That's interesting. But they use Memcached, but I wanted to talk about Redis.
11:30 So I've not used Redis myself, but I know that a lot of people do for caching and for other things.
11:38 So this is an article, it's actually on the Redis site, but it's an article called "Redis Beyond the Cache" in Python by Guy Royce, I think.
11:48 I knew that Redis did more than just a cache for a back-end database.
11:53 But this is kind of neat.
11:55 These are good, clear examples of Python code using Redis for more than just caching.
12:01 The first example talks about how to use it as a queue.
12:04 So you can set it up as a fast queuing system.
12:07 Apparently, there's a couple calls called rpush and blpop.
12:12 And actually, to tell you the truth, I picked this article because of BLpop.
12:16 I think that's one of the best function names ever.
12:19 I don't know what it means, but maybe back of the list pop?
12:22 Not sure. But it's good.
12:24 I thought you picked it because of the various--
12:27 from the code example about putting stuff into queues here.
12:31 That felt close to home.
12:33 Did it?
12:34 Yeah, it's about Bigfoot sightings.
12:36 We've got a sighting near the Columbia River, and people were chased by a tall, hairy creature, and so on.
12:41 And so on, so like asynchronously adding Bigfoot sightings from the general Pacific Northwest.
12:47 Yeah, that's good.
12:48 Sorry, carry on, didn't mean to derail you.
12:51 No, no, it's good. So using it as a queue, using it in a Pub/Sub model, apparently there's functions like publish and psubscribe.
13:00 So you can do publish and subscribe models, data streaming, using it as a search engine.
13:06 The search engine seems like a little more hardcore because it looks like they're, It's almost like SQL queries that you're using, but apparently you can do that.
13:14 Of course, you can also use it as your primary in-memory database if you want to, as long as you don't need to store it somewhere, or use some later thing.
13:25 I guess I'm just swinging it here.
13:26 I don't know how you hook up a Redis database to a normal database, but I know you database people know how to do that.
13:35 But I like the idea of using it as a queue system for like multi threads and multi processes.
13:42 That sounds kind of fun.
13:43 - This is a really cool article because I just often think of Redis as cache, right?
13:48 But yeah, there's a bunch of neat stuff here.
13:50 And so often you think like, oh, I'll just write this cool data structure and we'll just do this thing and it's great.
13:56 And you're like, oh wait, but hold on.
13:59 When I deploy that to the web server, it forks off like 10 copies of micro-WSGI.
14:04 And so I'm gonna have like 10 separate DB copies and all this, like, there's just certain times you're like, I just need a thing to hold this stuff and like Redis seems pretty cool for that.
14:12 - Yeah, and the examples used, apparently there's a bunch of different Python libraries to access Redis, and this one uses AIO Redis because there's async and await calls to access everything.
14:23 - Yeah, it's beautiful.
14:24 It's a real nice example of async and await as well.
14:26 - Yeah.
14:27 - So I'm sure Brian, you've heard of little Bobby Tables.
14:31 - Yeah, of course.
14:33 I think we brought it up on the show.
14:35 Yeah, I don't know if we've actually, have we featured it as a proper joke?
14:39 I don't know what we have.
14:40 Nonetheless, this one is no joke.
14:42 This is just little table.
14:44 I didn't know what I was thinking.
14:46 I know what I was thinking.
14:46 I was curious.
14:48 I didn't want to commit as much effort as it turned out to be into having like a broad discussion about this.
14:54 But I thought, okay, well, we have dictionaries.
14:56 And so I can go and find a single key, pass in a certain key, and then get the thing back or not, right?
15:02 So if I've got like, I don't know, users, I could have the user ID and then the user object comes back.
15:10 If I index the dictionary like that, totally simple, right?
15:12 What if we wanted to ask that question two ways on the same data structure?
15:16 What if I wanted to say, give me the user by ID and give me the user by email.
15:22 So, so one possible way, I guess you could just cram all the IDs and all the emails into the dictionary, but then things like, you know, enumerate over predict.items breaks because you get, you know, every now and then it's integers or it's strings and then it's a duplicate of the users, like in.items or .values.
15:43 So it's not really a great one.
15:45 So I said, does Python have like a structure that is not a database?
15:49 Because I do not want to do database stuff.
15:52 Like if I wanted to do that, I would just use a database.
15:55 A thing that is lightweight in memory and easy to use that lets me put something like a user in there, but then be able to ask, give me the user by ID, give me the user by email, that is fast, right?
16:08 So dictionaries work because they're indexed and they're insanely like near, you know, a one type of performance on getting back the content that's in there, right?
16:19 So I wanna be able to do that both with email and ID, not.
16:22 I'm gonna go on this rant some more later.
16:24 I'm actually trying to pull together all the responses I got, 'cause I got a bunch of things given back to me.
16:29 A lot of people suggested pandas, but I wanna store non-tabular data.
16:33 So I'm not sure pandas, which is tabular-ish, makes sense.
16:37 Nonetheless, one thing I did come up with that's probably the closest to what I was asking for without me doing any work, which I'm not against doing work, but if something exists, you know, let me pip install it, right?
16:48 Is this thing called Lighttable by Paul McGuire.
16:51 Sorry, not Lighttable, Littletable.
16:53 (laughs)
16:54 Littletable.
16:55 And it gives you a schema-less in-memory thing that's kind of like a dictionary, but gives you ORM-like access to the objects.
17:04 - Okay. - Okay, so it's like, think of like an in-memory database, basically, that you don't have to go create table, you know, set column type, name this to, you know, var char 16 type, and like, you don't have to actually define the table, like a full-on database, right?
17:22 You just say it has, you know, put these things in it, like you would a dictionary, and then you can access all the elements.
17:28 What do you think?
17:29 - I think I'd like to try to solve your problem also.
17:32 - It's a fun programming problem, right?
17:34 But this thing is pretty cool 'cause it lets you do like greater than queries.
17:38 It has indexes on all of the columns or the columns that you say you want them on.
17:43 All you do is say, it's like creating a dictionary and say, I'm gonna put in a thing by ID, I'm gonna put in a thing by email, I'm gonna put in a thing by city, and I want to index for all of those.
17:51 So it's like dictionary-like speed, which is pretty cool.
17:55 It even does like in-memory joins and all sorts of stuff.
17:59 So yeah.
18:01 - Okay.
18:02 - Yeah.
18:02 And the result of like a query can be like another little table.
18:06 So I could like do a filter and select only a couple of columns and then out comes a little baby little table, a little even littler little table.
18:14 Anyway, I thought this was a pretty cool thing because it lets you kind of do database like stuff without the effort, right?
18:22 Do it dynamically.
18:23 Some people said, "Hey, you should just use SQLite." I'm like, "Yeah, SQLite's cool, "but then I've got to come up with a full-on schema "for defining the thing." And that gets to be a pain.
18:34 There's also some other options, but little table, it looks good.
18:38 - Yeah, I'll have to get an example, get your actual problem statement again and try it, but this looks neat.
18:44 - Yeah, yeah, absolutely.
18:45 Yeah, well, I'll come back to that for sure as well.
18:48 'Cause I wanna bring, like I got so many good recommendations and ideas that I think it's probably worth just doing a segment on that, but little table.
18:55 - Nice.
18:56 This is something I'm surprised we didn't talk about already and maybe we have, but I've forgotten.
19:01 pytest Timeout.
19:02 This was a listener suggestion and I think it's pretty much an essential plugin for any test suite that you're running, especially if it's not something you're running where you're watching it.
19:14 So if it's something running on a server or continuous integration or something, or if it's a long running test suite, It's a very simple to use plugin.
19:24 And what you want to make sure is that none of your tests run longer than a certain number of seconds.
19:29 All the people out there that are like scratching your head thinking, wow, there's a test that runs longer than a second.
19:36 Yes, there are tests that run longer than a second.
19:38 - Especially if they're trying to talk to hardware or external things and that thing might not be there and it's just waiting.
19:44 - Yeah, there's more to testing than unit testing.
19:46 There's also system testing.
19:48 But anyway, this one's great 'cause you can set up configuration in the config file you can throw one number in to say like say you have like you know five minutes or something like that or or even just down to like three minutes I don't want to make I want to make sure nothing nothing runs longer than this and just to make sure that the server doesn't just sit spinning all night long and then well let's say you even tighten it closer to try to kill off a test if it's running longer than a certain amount but there there's like maybe two of your tests that are longer or a few of them that are longer, you can put a decorator on those particular tests and give them more time and then the rest of them shorter.
20:27 It's very easy to operate and just kind of a must-have for long test suites.
20:32 Yeah, that's super cool.
20:33 Yeah, I mean, sometimes you just rather have the test fail if it's taking way, way, way too long and you're like, "I'm pretty sure this is going to fail," but not right away.
20:41 I would recommend just trying it out and kind of like look at the time of your tests and stuff and then set it so that it actually kills one of your tests in the middle or stick a spin in there or something like that just to verify it does because it is sort of operating system dependent and there's some configuration allowed in the plugin to be able to use either signals or kill commands or process killing. There's different ways to stop a test that's going too long and and that's so test it before you deploy it, but it's a good thing.
21:15 - Do a meta test.
21:15 - Yeah, test of your test.
21:17 - Exactly.
21:18 Super cool.
21:19 Okay, that's a great one.
21:20 And you know, use case is super straightforward.
21:23 I have got one for you that has got me really, really excited.
21:26 It's called events.
21:27 So in Python, we have functions as first class objects, right?
21:32 You can pass a function around super easy, right?
21:35 Like if there's some part of your program is gonna run you want to get a function called when it's done, you can pass that function, it'll do its work, you can call it, right?
21:44 You have this kind of, this observer style programming, right?
21:48 What requires programming on your behalf is to have that happen for more than one thing.
21:53 Like I would like parts of my program to subscribe to being notified about events, and one or more of them get called when this thing happens.
22:03 So a friend of mine, Nicola Aroshi, put together a really cool project called events.
22:11 And the idea is that it adds event subscription and callback to the Python language.
22:16 In like a super simple way.
22:17 So go to a function that is an event.
22:20 If I want my function to be called by it, I would say like, if I want the event on change, I would say my class dot on change plus equals some function to call.
22:32 And if there's already one there, it's just gonna add it to the list of all the functions that'll be called when that event fires.
22:37 And if at some point I decide I don't wanna hear about it anymore, I just go to my class dot or my object dot on change minus equals the function I wanna take out of that subscription list.
22:47 And that's it.
22:48 - Oh, that's neat.
22:49 - Isn't that slick?
22:50 And then to call it, you just say object dot on change and you pass the arguments and then all those functions get called in order.
22:56 - Oh, this is cool.
22:57 - Yeah, so it's, if you have to do any sort of observer design pattern, event subscription stuff, like this is super, super nice.
23:05 - And it's inspired on the C# language base event keyword, which is based on delegates, basically function pointers.
23:12 It doesn't really matter if you know about that or care about it, but if you know about the C# version, this basically brings that to the Python language.
23:18 - Yeah, I kind of want to build up a finite state machine using this.
23:21 - It's cool, right?
23:22 - Yeah, I mean, and it could make it really readable.
23:25 - Yeah, I have a gist that I'm working on, or I have some code I'm working on, I'll post as a gist that people can check out that is like a lot better than what they have in the documentation.
23:35 So the documentation takes like this raw event source and shows you how you can subscribe and unsubscribe to it.
23:40 But what I've got is something that's like, here's how you have a class, right?
23:44 Like, you know, a thing on the screen, and then you could have like subscribe to when the location changes or the size changes, or, you know, those kinds of things.
23:52 And it's more of like a natural programming analogy.
23:56 So I'll put up the gist for that.
23:58 I'm just working on a few things to see if I can make it even slightly better.
24:01 I'm seeing if I can use descriptors so that the event triggering happens behind the scenes without you even have to program it as well.
24:08 So like right now, from the outside, using it is really easy, but you do have to sort of like know when something's changed and then call that, raise that event.
24:17 I think I can use descriptors to maybe make it seamless on both sides, but I'm still playing with that.
24:23 - Now, do you know if all of the events get called by the thing changing, the making the event happen?
24:29 - Yes. - They do?
24:30 - Yeah. - Okay.
24:31 - They get called by the thing that, whatever decides to raise the event, that's the thing that's doing the calling.
24:38 The events just basically manage what are the functions to be called in what order, and then you call it and it just delegates onto them.
24:46 Also, you get to just arbitrarily pick the parameters that get passed along.
24:52 But it seems like a good idea to say, this event always takes these kinds of arguments and whatever, there's not a lot of structure there.
24:59 You do get the only real safety is you can say, when you create it, you can say, these are the only allowed events because it's kind of just full on dynamic programming.
25:09 But you can say these three things, you can subscribe and unsubscribe and call.
25:14 Anything else, we're gonna say it doesn't exist.
25:16 So that's pretty nice.
25:17 - Yeah, yeah.
25:18 - Yeah, it provides a little safety.
25:19 - Cool. - Yeah.
25:20 - Well, that's our six items.
25:22 Do you have any extras for us?
25:23 - Not really.
25:24 Just, I sort of talked about it.
25:25 I was gonna talk about it here, but I talked about it in the, where we talked about what we're doing, how people can support us.
25:30 I finished the Python memory management course.
25:32 The thing is so cool.
25:34 It's a five hour course, just diving into the internals of like Python memory management algorithms.
25:39 And what I thought I would create was something that was like understanding Python memory management, but there's actually a ton of techniques I discovered that actually let you run your code in a way that's like, well, now it uses half as much memory and it's 30% faster and stuff like that.
25:54 So I didn't think there would be a lot of actionable stuff coming out of it, but there is, which I think is pretty cool, actually.
25:59 - Oh, nice. - Yeah. How about you?
26:01 I'm pretty excited that pytest 6 is out.
26:04 A couple weeks ago, we talked about the 6 being in sort of a beta release, but it's out now.
26:10 And I wanted to mention that episode 125 of Testing Code walks through those changes.
26:16 This is due to the miracles of time travel.
26:20 This has not been recorded yet, but it will be recorded and released by last week.
26:25 [laughs]
26:26 - Perfect, time travel, I love it.
26:29 You've chosen the perfect joke.
26:31 So the only question I have for you before we do the joke is am I the school administrator IT person or am I the mom?
26:39 - Oh, you be the mom.
26:40 - Okay. - Okay.
26:41 - So the phone rings, I pick it up.
26:42 - Yeah, hi, this is your son's school.
26:45 We're having some computer trouble.
26:46 - Oh dear, did he break something?
26:48 - In a way, did you really name your son Robert, - Robert, single quote, parentheses, semicolon, drop table, students, semicolon, minus minus.
27:01 - Oh yes, little Bobby Tables, we call him.
27:03 - Well, we've lost this year's student records.
27:06 I hope you're happy.
27:07 - And I hope you've learned to sanitize your database inputs.
27:10 Be on the lookout for that SQL injection, baby.
27:12 I love it, this is so good.
27:15 This is absolutely one of the most classic computer jokes there is.
27:19 - Yeah, I love it.
27:20 - Because it probably would actually work.
27:23 It reminds me of the guy who said that his, he got his license plate to be the, the characters in U L L no.
27:32 Yeah.
27:33 I heard about that.
27:34 Yeah.
27:34 And he ended up getting all the like automated, you know, you drove through a traffic light sort of thing, tickets for all the records that were no.
27:43 Yeah.
27:44 Or any time you didn't have data with them, any police officer that forgot to enter the license plate, it would go to him.
27:53 He thought he would get out of it because they wouldn't be able to listen to him, but oh no.
27:56 [laughter]
27:58 - That's hilarious. - Awesome, awesome.
28:00 All right, well, great to chat with you as always.
28:02 - All right, you too. Bye. - Bye.
28:04 Thank you for listening to Python Bytes. Follow the show on Twitter @pythonbytes.
28:09 That's Python Bytes as in B-Y-T-E-S.
28:12 And get the full show notes at pythonbytes.fm.
28:15 If you have a news item you want featured, just visit pythonbytes.fm and send it our way.
28:20 We're always on the lookout for sharing something cool.
28:22 This is Brian Okken, and on behalf of myself and Michael Kennedy, thank you for listening and sharing this podcast with your friends and colleagues.
28:28 [BLANK_AUDIO]