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.
00:10 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.
00:18 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.
00:27 No. So mutation testing.
00:29 So I really kind of, I think, I'm warming to mutation testing, and it's kind of a neat thing.
00:35 And I think we've covered it before, but this article is from Mosh Zadka, and it's called An Introduction to Mutation Testing in Python.
00:42 There are a few, a handful of, I think there's like two or three different mutation testing libraries.
00:48 MuttMutt is one of them, and that's what this article uses.
00:52 And so if people are not familiar with mutation testing, here's the problem.
00:57 So you can use code coverage tools like coverage.py to show how much of your code your tests are covering.
01:04 But even if you get to 100% coverage, it doesn't mean that you're really testing everything.
01:09 And so mutation testing, what it does is it takes your code under test, and it does some modifications.
01:16 So it modifies portions of your source code to simulate potential bugs.
01:21 Like, for example, it'll replace the greater than comparison with greater than equal, or replacing it with those sorts of edge cases and stuff are often where we muck up.
01:30 If there's no boundary test around the boundary condition, you know, there'll be a problem.
01:35 So every little change is considered a mutant.
01:38 And it generates all these different mutants, and it does it in a fairly smart way.
01:43 It can test your code fairly quickly with not too many mutants.
01:47 So, and then it runs your test suite on the mutant.
01:50 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:01 But he runs mutt-mutt, and 16 of them survive, and then talks about how to fix that.
02:08 So it's a 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.
02:19 You know, one thing that I don't understand about mutation testing is, like, I understand, okay, well,
02:24 we're going to change, like, a value of a variable, or, like, the way if we're doing a test,
02:30 make it, it was less than, we're going to make it greater than, and see if your tests still pass,
02:34 and, like, those kind of things.
02:35 That totally seems reasonable.
02:36 But if it goes in, I don't know if it does, maybe, you know, if it goes and, like, says,
02:40 well, you're doing a print statement, so we changed part of the print string.
02:44 Like, who's testing for that, right?
02:46 Like, that seems like it would survive.
02:48 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
02:54 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 Yeah.
03:02 But I feel like the sort of general case of mutation testing, you go, well, here's a piece of variable
03:07 that I need to change around.
03:08 Let's change a string and see if the tests still pass.
03:11 So, I don't know, maybe it's just inappropriate for those types of scenarios.
03:14 Maybe you only test stuff, like, at a lower level where you don't have, like, a bunch of print statements.
03:18 But, you know, 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:29 I see.
03:30 So, I don't know if it does the print statement sort of example, but I'm sure that there's ways
03:34 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:40 Yeah.
03:41 Like, don't modify constants or something, maybe.
03:43 Who knows?
03:44 Yeah.
03:44 Yeah.
03:45 Cool.
03:45 All right.
03:46 Well, you know, that looks really interesting.
03:47 And Masha does a great job writing up these types of things.
03:50 We feature him a lot.
03:51 Very cool.
03:52 Next up, I want to talk about asynchronous programming.
03:56 Oh, nice.
03:57 Yeah.
03:57 So, maybe we've covered this before.
03:59 No, we've covered this a lot, but I don't believe we've covered async Q.
04:03 I don't think so.
04:04 I don't think so either.
04:05 So, this is from Quora, and it is not brand new.
04:10 So, I just want to be really upfront.
04:11 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
04:22 and await programming is about, is scaling while you're waiting.
04:26 Scaling the latencies, right?
04:28 Okay.
04:29 So, you know, like, I'm going to call the Stripe service, and it's going to take, you
04:34 know, half a second to return.
04:35 And so, I want my web server to be able to go and just do stuff.
04:39 You know, other requests instead of waiting for half a second while we're checking out
04:43 some person or whatever, right?
04:44 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.
04:50 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
05:07 slow.
05:07 Don't give me started on that.
05:09 But what they're doing is they're talking to Memcached or Redis or whatever, but they're
05:15 using Memcached to store a bunch of pre-computed query results so they don't have to keep going
05:22 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
05:29 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.
05:35 We store the IDs of the upvoter.
05:37 Then we're going to do a query, a join over on the user table and get their names back.
05:42 And then we're going to show it.
05:43 Like, that sounds expensive for lots of data.
05:45 So what they do is they basically store those answers.
05:48 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
06:01 names over and over, right?
06:03 Because the way that you store stuff in Memcached is this ID has this name.
06:07 You've got 50 upvoters.
06:09 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, you know, a batch get like, here's all these IDs.
06:15 Go get me all the associated names.
06:17 And they've got like this dependency tree of these sorts of questions they have to answer.
06:22 So what they've done is they've come up with this thing called AsyncQ.
06:25 And it's all about batching asynchronous requests and converting them from a bunch of individual
06:30 calls into like one massive call.
06:32 Oh, okay.
06:33 So they can like do what looks like asynchronous programming.
06:37 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 that
06:43 goes, okay, what that means is turn that into one giant query where it's like all of these
06:48 IDs go to all those things and then return them back.
06:51 Oh, that's right.
06:51 Yeah.
06:51 It's pretty neat.
06:52 So it's basically this way to write code that will take a, what would be a bunch of small
06:57 independent requests and turn it into like a one shot request for talking to things like
07:03 caching servers and whatnot.
07:04 Yeah.
07:05 Yeah.
07:05 So apparently this is like a core component of core's architecture and yeah, it's all about
07:11 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, like an engineering blog where they
07:20 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
07:26 were doing before this, which was, you'd have to like write several functions that would
07:30 prepare the things.
07:31 And then you could ask for it because they would be cached locally and all sorts of funky
07:35 stuff.
07:35 So there's a great writeup 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:43 And so they use the yield keyword, which is a sort of a more foundational way to get, to
07:50 break up functions into parts that run.
07:52 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.
08:01 And then it like batches it up and then does it all at once.
08:04 Pretty wild.
08:04 Yeah.
08:05 Yeah.
08:05 So I thought this was, you know, kind of interesting.
08:08 I think it's a little bit just looking at the patterns here.
08:12 I feel like it's a little tiny bit limited because it's targeted at, I believe they're still at
08:19 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
08:25 Python two, right?
08:26 Remember this 2016, they'd been running for a while.
08:28 So some of their APIs, I don't think like, for example, they don't use the async and await
08:34 keyword.
08:35 I think because that didn't exist.
08:38 Like they supported Python three, four where asyncio was, but async and await didn't come
08:43 along until just a tiny bit later.
08:44 I don't think so.
08:45 Anyway, a bit of a grain of salt, but I think, you know, this will be a pretty interesting thing
08:50 that people can adopt and use for these types of scenarios.
08:53 Certainly if it powers Quora, it's probably pretty good.
08:57 Yeah.
08:57 Neat.
08:58 Cool.
08:58 Absolutely.
08:59 Yep.
08:59 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:06 course for people who generally live in Excel and should be adopting the Python data science
09:11 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:21 I finished it.
09:22 I recorded it.
09:23 It's like a five hour course.
09:24 It's going to be awesome.
09:24 So now I'm on to writing a new course, Python design patterns.
09:28 Oh, nice.
09:29 Yeah.
09:29 So that'll be out in a few weeks as well.
09:31 How about you?
09:32 Yeah.
09:32 I just wanted to highlight again.
09:35 I have the URL pytestbook.com set up to go directly to Arata because I get a lot of people
09:42 asking, hey, your pytest book, is it still good for, it was like in 2017, still valid?
09:48 Yes, it's still valid.
09:50 But there's a few gotchas and I list them out.
09:53 Very easy to read at pytestbook.com.
09:56 It directs you to an Arata page to show you.
09:59 It's just a couple tweaks to the source code you got to make.
10:02 You got to pin TinyDB and a couple other things.
10:05 And we'll try to get those changes out to the download link on Pragmatic as soon as possible.
10:11 That's still in the works.
10:13 But there's also a link to, if you have any issues, there's a link to the official Pragmatic
10:18 Arata page where you can ask questions.
10:21 And if you haven't run into anything, I'd love to hear about it.
10:24 And I'm excited to get a lot.
10:26 Lately, a lot of the people that have been contacting me said they're excited about reading the book
10:31 are machine learning people.
10:32 So it's kind of neat to see data science and machine learning people add testing to their workflows.
10:38 That's exciting.
10:39 Absolutely.
10:39 So I have a final call to action for people out there.
10:42 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,
10:48 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.
10:59 Or if a company is doing a bunch of testing, have everyone on that team or the engineering group get Brian's book.
11:06 That would be great.
11:07 Yeah, and then individually to remind people that we do have a Patreon campaign going.
11:12 So people can contribute a buck or two a month.
11:16 That would be great.
11:17 Yeah.
11:17 Now that we go anywhere, we don't buy coffee.
11:19 Yeah.
11:20 Next, I want to talk.
11:21 This sort of ties into your async thing.
11:24 Yeah, yeah, for sure.
11:25 That's interesting.
11:26 But they use Memcached.
11:28 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 And so this is an article.
11:39 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 backend database.
11:53 But this is kind of neat.
11:55 So these are good, clear examples of Python code using Redis for more than just caching.
12:00 So 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 And 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.
12:23 But it's good.
12:24 I thought you picked it because of the various, from the code example about putting stuff into queues here.
12:31 That felt close to home.
12:34 Did it?
12:34 Yeah.
12:34 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:42 Oh, yeah.
12:42 So like asynchronously adding Bigfoot sightings from the general Pacific Northwest.
12:46 Yeah, that's good.
12:48 Sorry, carry on.
12:50 Didn't mean to derail you.
12:51 No, no.
12:52 It's good.
12:53 So using it as a queue, using it in a PubSub model.
12:56 Apparently there's functions like publish and psubscribe.
13:00 So you can do publish and subscribe models.
13:03 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.
13:12 But apparently you can do that.
13:14 And 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.
13:21 So, or use some later thing.
13:24 You know, I guess I'm just winging it here.
13:26 I don't know how you get, how you hook up a Redis database to a normal database.
13:31 But I know you database people know how to do that.
13:34 But I probably use, 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:47 But yeah, there's a bunch of neat stuff here.
13:49 And yeah.
13:51 So often you think like, oh, I'll just write this cool data structure.
13:55 We'll just do this thing.
13:55 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 whiskey.
14:04 And so I'm going to have like 10 separate DB copies and all this, like, there's just certain
14:08 times you're like, I just need a thing to hold this stuff.
14:10 And like Redis seems pretty cool for that.
14:12 Yeah.
14:12 And the examples use, apparently there's a bunch of different Python libraries to access Redis.
14:17 And this one uses AIO Redis because there's async and await calls to access everything.
14:23 Yeah.
14:23 It's beautiful.
14:24 It's a real nice example of async and await as well there.
14:26 Yeah.
14:27 So I'm sure, Brian, you've heard of little Bobby tables.
14:30 Yeah, of course.
14:33 I don't know.
14:34 I think we've brought it up on the show.
14:35 Yeah.
14:35 I don't know if we've actually, have we featured it as a proper joke?
14:38 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 don't know what I was thinking.
14:45 I know what I was thinking.
14:46 I was curious.
14:47 I didn't want to commit as much effort as it turned out to be into having like a broad discussion about this.
14:53 But I thought, okay, well, we have dictionaries.
14:55 And so I can go and find a single key, pass in a certain key, and then get the thing back or not.
15:01 Right?
15:02 So if I've got like, I don't know, users, I could have the user ID.
15:07 And then the user object comes back if I index the dictionary like that.
15:11 Totally simple, right?
15:12 Yeah.
15:13 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 one possible way, I guess, you could just cram all the IDs and all the emails into the dictionary.
15:29 But then things like, you know, enumerate over dict.items breaks because you get, you know, every now and then it's integers or it's strings.
15:38 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:54 A thing that is lightweight and 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.
16:05 Give me the user by email.
16:07 That is fast.
16:08 Right.
16:08 So dictionaries work because they're indexed and they're insanely like near, you know, oh, one type of performance on getting back the content that's in there.
16:18 Right.
16:18 Yeah.
16:18 So I want to be able to do that both with email and ID not.
16:21 I'm going to go on this rant some more later.
16:24 I'm actually trying to pull together all the responses I got because I got a bunch of things given back to me.
16:29 A lot of people suggested pandas, but I want to 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.
16:45 But if something exists, you know, let me pip install it.
16:47 Right.
16:47 Is this thing called light table by Paul McGuire.
16:50 Sorry, not light table.
16:52 Little table.
16:52 Little table.
16:54 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.
17:04 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.
17:17 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:31 It's a fun programming problem, right?
17:34 But this thing is pretty cool because 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:42 Like, all you do is say, it's like create a dictionary and say, I'm going to put in a thing by ID.
17:46 I'm going to put in a thing by email.
17:47 I'm going to put in a thing by city.
17:49 And I want to index for all of those.
17:51 So it's like dictionary-like speed, which is pretty cool.
17:54 It even does like in-memory joins and all sorts of stuff.
17:59 So, yeah.
18:01 Okay.
18:01 That's neat.
18:02 And the result of like a query can be like another little table.
18:05 So I could like do a filter and select only a couple of columns.
18:09 And then out comes a little baby little table.
18:11 A little, even littler little table.
18:13 Anyway, I thought this was a pretty cool thing because it lets you kind of do database-like stuff without the effort.
18:21 Right?
18:22 Do it dynamically.
18:23 Some people said, hey, you should just use SQLite.
18:26 I'm like, yeah, SQLite's cool.
18:28 But then I've got to come up with a full-on schema for defining the thing.
18:32 And that gets to be a pain.
18:34 There's also some other options.
18:36 But little table looks good.
18:38 Yeah.
18:38 I'll have to get an example.
18:40 Get your actual problem statement again and try it.
18:43 But this looks neat.
18:44 Yeah.
18:44 Yeah, absolutely.
18:45 Yeah, well, I'll come back to that for sure as well.
18:47 So because I want to bring together, like I got so many good recommendations and ideas that I think is probably worth just doing a segment on that.
18:54 But little table.
18:55 Nice.
18:56 This is something I'm surprised we didn't talk about already.
18:58 Maybe we have, but I've forgotten.
19:00 pytest Timeout.
19:02 This was a listener's suggestion.
19:03 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:23 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 their head thinking, wow, there's a test that runs longer than a second.
19:35 Yes, there are tests that run longer than a second.
19:38 Especially if they're trying to talk to hardware or external things.
19:42 Yeah.
19:42 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 because you can set up a configuration in the config file.
19:53 You can throw one number in to say, like, say you have, like, you know, five minutes or something like that.
19:58 Or even just down to, like, three minutes.
20:01 I want to make sure nothing runs longer than this.
20:05 And just to make sure that the server doesn't just sit spinning all night long.
20:10 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.
20:16 But there's, like, maybe two of your tests that are longer or a few of them that are longer.
20:21 You can put a decorator on those particular tests and give them more time.
20:25 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'd just rather have the test fail if it's taking way, way, way too long.
20:39 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.
20:46 And then set it so that it actually kills one of your tests in the middle.
20:51 Or stick a spin in there or something like that.
20:54 Just to verify it does.
20:55 Because it is sort of operating system dependent.
20:58 And there's some configuration allowed in the plug-in to be able to use either signals or kill commands or process killing.
21:05 There's different ways to stop a test that's going too long.
21:11 So test it before you deploy it.
21:13 But it's a good thing.
21:15 Do a meta test.
21:15 Yeah.
21:16 Test of your test.
21:17 Exactly.
21:17 Super cool.
21:19 Okay, that's a great one.
21:20 And, you know, use case is super straightforward.
21:22 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 that's going to run and you want to get a function called when it's done,
21:41 you can pass that function, it'll do its work, you can call it, right?
21:44 You have this kind of observer style programming, right?
21:48 Yeah.
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:10 And the idea is that it adds event subscription and callback to the Python language.
22:15 Oh, cool.
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, 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 going to add it to the list of all the functions that will be called when that event fires.
22:37 And if at some point I decide I don't want to hear about it anymore, I just go to my class dot or my object dot on change minus equals the function I want to take out of that subscription list.
22:47 And that's it.
22:48 Oh, that's neat.
22:48 Isn't that slick?
22:49 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.
22:57 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:11 It doesn't really matter if you know about that or care about it.
23:14 But if you know about the C# version, this basically brings that to the Python language.
23:18 Yeah.
23:18 I kind of want to build up a finite state machine using this.
23:21 It's cool, right?
23:22 Yeah.
23:22 I mean, it could make it really readable.
23:25 Yeah.
23:25 I have a gist that I'm working on or I have some code I'm working on.
23:29 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.
23:47 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:57 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 the from the outside using it is really easy.
24:12 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.
24:20 But I'm still playing with that.
24:22 Now, do you know if this if all of the events get called by the thing changing the making the event happen?
24:29 Yes.
24:29 They do?
24:30 Yeah.
24:30 Okay.
24:30 Yeah.
24:30 Yeah.
24:30 So they get called by the thing that whatever decides to raise the event.
24:35 That's the thing that's doing the calling.
24:37 The events just basically manage what are the functions to be called in what order.
24:43 And then like you call it and it just like delegates onto them.
24:46 Also, you get to just arbitrarily pick the parameters that gets passed that get passed along.
24:51 But it seems like a good idea to say this event always takes these kinds of arguments and whatever.
24:56 There's not a lot of structure there.
24:58 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 anything else.
25:14 We're going to say it doesn't exist.
25:16 So that's pretty nice.
25:17 Yeah.
25:17 Yeah.
25:18 Yeah.
25:18 It provides a little safety.
25:19 Cool.
25:20 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 going to talk about it here.
25:26 But I talked about it in the 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.
25:44 But there's actually a ton of techniques I discovered that actually let you run your code in ways that are 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.
25:56 But there is, which I think is pretty cool, actually.
25:59 Oh, nice.
26:00 Yeah.
26:00 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:19 This has not been recorded yet, but it will be recorded and released by last week.
26:25 Perfect.
26:27 Time travel.
26:28 I love it.
26:28 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:38 Oh, you be the mom.
26:40 Okay.
26:40 Okay.
26:40 So the phone rings.
26:41 I pick it up.
26:42 Yeah.
26:42 Hi, this is your son's school.
26:44 We're having some computer trouble.
26:46 Oh, dear.
26:47 Did he break something?
26:48 In a way.
26:49 Did you really name your son Robert?
26:52 Robert single quote, parentheses, semicolon, drop table, students, semicolon, minus, minus?
27:01 Oh, yes.
27:01 Little Bobby Tables, we call them.
27:03 Well, we've lost this year's student records.
27:05 I hope you're happy.
27:06 And I hope you've learned to sanitize your database inputs.
27:10 Be on the lookout for that sequel injection, baby.
27:12 I love it.
27:14 This is so good.
27:15 This is absolutely one of the most classic computer jokes there is.
27:18 Yeah.
27:19 I love it.
27:20 Because it probably would actually work.
27:22 It reminds me of the guy who said that he got his license plate to be the characters N-U-L-L,
27:32 null.
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 null.
27:43 Yeah.
27:44 Anytime they didn't have data, it went to him.
27:47 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 send it to him.
27:55 But oh, no.
27:56 That's hilarious.
27:59 Awesome.
28:00 Awesome.
28:00 All right.
28:01 Well, great to chat with you as always.
28:02 All right.
28:03 You too.
28:03 Bye.
28:04 Bye.
28:04 Thank you for listening to Python Bytes.
28:06 Follow the show on Twitter at Python Bytes.
28:08 That's Python Bytes as in B-Y-T-E-S.
28:11 And get the full show notes at Python Bytes.fm.
28:14 If you have a news item you want featured, just visit Python Bytes.fm and send it our way.
28:19 We're always on the lookout for sharing something cool.
28:21 This is Brian Okken.
28:23 And on behalf of myself and Michael Kennedy, thank you for listening and sharing this podcast with your friends and colleagues.
28:28 Thanks.