WEBVTT

00:00:00.001 --> 00:00:04.380
Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to

00:00:04.380 --> 00:00:12.480
your earbuds and mine. And this is episode 417, recorded January 21st, 2025. And I'm Brian Okken.

00:00:12.480 --> 00:00:13.800
And I am Michael Kennedy.

00:00:13.800 --> 00:00:20.300
And we're excited about this show today. And nothing ain't nothing going to bring us down.

00:00:20.300 --> 00:00:26.860
So but before we get started, I want to thank everybody that has supported us through

00:00:26.860 --> 00:00:33.760
Talk Python training through pythontest.com, the courses through ByMyBook, our Patreon supporters,

00:00:33.760 --> 00:00:39.400
of course, you rock. And of course, many of the sponsors that have sponsored us in the past.

00:00:39.400 --> 00:00:43.200
And we love them too. But we also love people that support us directly.

00:00:43.200 --> 00:00:49.120
If you'd like to send us topics, please do so through there's contact form on our website.

00:00:49.120 --> 00:00:56.200
But also you can send them to us at Bluesky or on Mastodon. And those links are in the show notes.

00:00:56.620 --> 00:01:02.880
And if you are listening to this, thank you. And also share it with a friend. And if you'd like to

00:01:02.880 --> 00:01:08.240
join us live sometime, check out pythonbytes.fm/live to see when the next episode is going to

00:01:08.240 --> 00:01:16.640
be filmed, filmed and recorded. And you can join us and comment while we're going live. And thank you

00:01:16.640 --> 00:01:24.140
also for people that subscribe to the email newsletter. If you go to pythonbytes.fm, you could subscribe there

00:01:24.140 --> 00:01:30.500
as well. And get the list of all the topics directly in your inbox so you don't have to go look those up.

00:01:30.500 --> 00:01:34.880
Yeah, we're evolving the format of that a little bit, trying to make a little deeper analysis,

00:01:34.880 --> 00:01:39.080
but also skimmable. And yeah, it's a huge resource. I think it's great.

00:01:39.080 --> 00:01:39.460
Yeah.

00:01:39.460 --> 00:01:43.200
People listen as well, but it's also nice to just have that written down in one place.

00:01:43.400 --> 00:01:48.520
And we cover lots of great topics every week. And what is our first topic this week, Michael?

00:01:48.520 --> 00:01:57.360
The first topic will be the LLM catcher. The name, not terribly descriptive of what it actually does,

00:01:57.360 --> 00:02:03.660
but here's the deal. I'm sure everyone has done this at this point. I know I've done it recently as I was

00:02:03.660 --> 00:02:10.220
yelling at the Bodo 3 API because there ain't nothing as frustrating as a little bit of a little

00:02:10.220 --> 00:02:17.380
Bodo auto-generated, no comments, no documentation, no idea what parameters go in it. Anyway, you might

00:02:17.380 --> 00:02:25.020
take those errors and pass them over to an LLM and go, please, dear chat, co-pilot, anthropic,

00:02:25.240 --> 00:02:30.160
whatever. What is going on here? What am I missing? Right? And it's super helpful. But this

00:02:30.160 --> 00:02:34.920
project is a little bit different. It's like a gateway to those types of things. So here's what

00:02:34.920 --> 00:02:40.980
you get. If there is a crash, obviously you have stack traces or tracebacks, depending on the language

00:02:40.980 --> 00:02:49.480
you and how you say it. They describe it here as the unsung villains of debugging. Why wrestle with the

00:02:49.480 --> 00:02:53.960
wall of cryptic error messages when you could let LLM catcher do the heavy lifting? So here's the thing.

00:02:54.240 --> 00:02:58.420
You basically, I'll go down here somewhere. What you can do is in your try accept blocks,

00:02:58.420 --> 00:03:03.760
you can say, you know, given an exception, diagnose or dot diagnose, passing the exception,

00:03:03.760 --> 00:03:11.400
and it will pass those details over to various LLMs and say, help me understand this and print out a

00:03:11.400 --> 00:03:17.700
message that will show me how to fix it, not just traceback. Okay. So I don't know if I can find it.

00:03:17.700 --> 00:03:22.180
Yeah, I think it's pretty dope. I would not use it in production, though you could, you could,

00:03:22.240 --> 00:03:26.620
if you want your logs to have messages about here's actually what happened, it's your debugging

00:03:26.620 --> 00:03:32.680
sidekick. So what you do is you can run OLAMA locally, and that's the default, or if you give

00:03:32.680 --> 00:03:40.380
it your open AI API key, it can pass it over to whatever level of model you possibly have. You know,

00:03:40.380 --> 00:03:45.080
it'd be awesome if you could have a one mini or something like that, diagnose it over at ChatGPT.

00:03:45.220 --> 00:03:49.320
So there's different ones that we'll work with. But basically, when it gets an exception,

00:03:49.320 --> 00:03:55.620
it says, hey, I'm working on this thing with FastAPI, and I get this exception, help me figure out

00:03:55.620 --> 00:04:01.300
what's going on. So the OLAMA one is a local free just running your machine version, open AI, well,

00:04:01.300 --> 00:04:07.200
we know, all know about ChatGPT, right? So you can put it as a decorator on a function, you can manually

00:04:07.200 --> 00:04:12.780
do it in a try except block, or you can even register a global exception handler. So anytime

00:04:12.780 --> 00:04:19.240
a global exception happens, it's uncaught in your system, it'll diagnose it has both async and async

00:04:19.240 --> 00:04:24.340
API, and you can set it up through environment variables. So, you know, it shows you like how to

00:04:24.340 --> 00:04:31.720
pull down the qwen 2.5 coder model for OLAMA, which is pretty excellent. And just off it goes,

00:04:31.720 --> 00:04:36.420
look at that. So you've got your diagnoser.catch on a risky function, or in your try except block,

00:04:36.420 --> 00:04:41.660
you just say, you know, diagnose or async diagnose, because you know, it's going to run for a while,

00:04:41.660 --> 00:04:46.540
it's going to make an API call either locally or out to ChatGPT. So you don't want to necessarily

00:04:46.540 --> 00:04:51.040
block your system. So you just make a little async await, boom, off it goes. Yeah, there you go.

00:04:51.040 --> 00:04:57.520
That's pretty much it. You can get formatted or unformatted information back. So if you need plain

00:04:57.520 --> 00:05:01.460
text to go in some kind of JSON field, you can do that. Or you can get it with

00:05:01.460 --> 00:05:04.040
proper formatting to make it more readable. What do you think, Brian?

00:05:04.040 --> 00:05:09.180
I'm going to withhold judgment on this until I give it a shot at some point.

00:05:09.180 --> 00:05:15.460
But yeah, you can even specify the temperature, aka the creativity you want the model to apply to your

00:05:15.460 --> 00:05:18.900
analysis. That's funny. Yeah, it's an open AI thing.

00:05:18.900 --> 00:05:26.760
I kind of like it to like, on any exception, just upload my entire code base and rewrite my code to

00:05:26.760 --> 00:05:31.200
fix the error. Exactly. Don't diagnose it, just fix it. Why am I even in the way here?

00:05:31.200 --> 00:05:38.480
Yeah, so look at it. Yeah, you can even do the full on 01 model of ChatGPT, which is like the really,

00:05:38.480 --> 00:05:40.040
really high one. Is that the $200 one?

00:05:40.040 --> 00:05:48.120
That's the $20 one, but you only get to call it 50 times a week. So not too many errors. If you get the $200 one, you can call it all day long.

00:05:48.120 --> 00:05:57.940
Yeah, I'd like people to get the $200 one, put this in your CI and do it over all versions of Python so that we just fill up all of the data.

00:05:57.940 --> 00:06:04.300
And then we'll get an announcement of, oh, the entire West Coast is blacked out because we broke the power grid.

00:06:04.300 --> 00:06:05.220
Yeah.

00:06:05.220 --> 00:06:09.040
But anyway, I think it's interesting, right? Just plug that in.

00:06:09.040 --> 00:06:12.620
Yeah, it looks like it might be kind of fun.

00:06:12.620 --> 00:06:17.920
Yeah, it does look kind of fun. And this was recommended by Pat. So thanks, Pat, for sending that in. Pat Decker.

00:06:17.920 --> 00:06:19.900
Oh, and Pat's here. Thanks, Pat.

00:06:19.900 --> 00:06:20.220
Yeah.

00:06:20.220 --> 00:06:20.600
Yeah.

00:06:20.600 --> 00:06:22.500
Over here.

00:06:23.000 --> 00:06:28.080
Well, I kind of want to talk about bad packages a little bit.

00:06:28.080 --> 00:06:31.140
Like no Christmas presents for them or what's going on?

00:06:31.140 --> 00:06:44.860
Yeah, no wrapping paper. Actually, we are talking about wrapping. So I want to talk about the Python packaging index and malicious stuff. So let's scroll down here.

00:06:44.860 --> 00:06:55.280
There's in the in the there was a security and safety engineer first year in review. This was from Mike Fiedler and and he talked about a lot of stuff.

00:06:55.280 --> 00:07:01.880
But one of the things he talked about was quarantining. And this came out in August. But but I just am catching up.

00:07:01.880 --> 00:07:04.360
So it's like if they catch COVID or what's going on.

00:07:04.360 --> 00:07:14.300
No, it's it's you know, it's like bad packages. So if somebody says, you know, there's a there's there's malware in a in a package shouldn't be there.

00:07:14.300 --> 00:07:19.640
What do we do with it? And they used to like have the option to investigate it and then yank it.

00:07:19.640 --> 00:07:21.740
But it just sort of makes the whole thing go away.

00:07:21.740 --> 00:07:27.620
But there's a new process. And they just recently at the end of December wrote about it.

00:07:27.620 --> 00:07:33.940
And there's it's called Project Quarantine. And in this we're linking to an article that really talks about it.

00:07:33.940 --> 00:07:39.520
So if you're if you're worried about malicious packages and you're curious about what IPI is up to, go ahead and check this out.

00:07:39.520 --> 00:07:43.180
I'm not going to go through the whole thing. However, it is kind of interesting.

00:07:43.180 --> 00:07:51.880
So the idea is if we jump back down to like future improvements and automation, hopefully we'd have some sort of automated way.

00:07:52.100 --> 00:07:57.800
Like let's say a couple people report that a package has malware in it.

00:07:57.800 --> 00:08:07.280
Administrators of PyPI can go ahead and and and somehow have some litmus test to say or some thing to say rather quickly.

00:08:07.280 --> 00:08:09.620
Let's get this get this get this under control.

00:08:09.620 --> 00:08:12.000
And the quarantine doesn't delete the whole thing.

00:08:12.000 --> 00:08:20.500
It it puts the there's an API simple API that an admin can go in and say, hey, we're going to quarantine this project.

00:08:20.500 --> 00:08:22.320
And the package goes into quarantine.

00:08:22.320 --> 00:08:26.620
And at that point, there's a bunch of the bunch of stuff happens.

00:08:26.620 --> 00:08:30.540
The it's not installable, but the owner can still see it.

00:08:30.540 --> 00:08:34.180
And the only owner can can make I don't know if they can make changes.

00:08:34.180 --> 00:08:39.240
But, yeah, it's not modifiable while it's in quarantine, but they can see what's going on.

00:08:39.700 --> 00:08:45.460
Administrators can look at it and and determine whether or not there really is malware there.

00:08:45.460 --> 00:08:51.300
And possibly it's possible that, you know, we might have some bad actors reporting packages.

00:08:51.300 --> 00:08:59.800
So we don't want people to like report stuff that's fine and have things to remove just because they're angry about it or something.

00:08:59.800 --> 00:09:02.000
But that hasn't happened yet.

00:09:02.000 --> 00:09:05.460
So this has been in place for a little while.

00:09:05.460 --> 00:09:11.140
And looking at the statistics, it's been let's see since August.

00:09:11.140 --> 00:09:12.380
They put this in place.

00:09:12.380 --> 00:09:22.760
There's been 140 reported packages and they've been gone into quarantine and only one of them exited quarantine.

00:09:22.760 --> 00:09:28.120
And it's because why was it the there was obfuscated code in there.

00:09:28.120 --> 00:09:31.900
Then that's a violation of the PIPI acceptable use policy.

00:09:31.900 --> 00:09:33.380
Project owner was contacted.

00:09:33.840 --> 00:09:37.440
They fixed it because they just, I guess, weren't aware that you can't do that.

00:09:37.440 --> 00:09:38.480
That's a really interesting.

00:09:38.480 --> 00:09:40.000
I didn't know that was a policy.

00:09:40.000 --> 00:09:40.620
Yeah.

00:09:40.620 --> 00:09:48.940
Well, I mean, if you want to ship something that you I know there are companies out there that would like we would like to obfuscate our code, but we still want to make it
available.

00:09:48.940 --> 00:09:51.280
But we don't do it through PIPI.

00:09:51.280 --> 00:09:52.660
I guess don't do it through PIPI.

00:09:52.780 --> 00:09:52.980
Okay.

00:09:52.980 --> 00:09:53.340
Yeah.

00:09:53.340 --> 00:09:55.500
I don't want to pipe obfuscated code.

00:09:55.500 --> 00:09:59.820
So I understand that that's primarily a shield that malware hides behind.

00:09:59.820 --> 00:10:00.060
Right.

00:10:00.060 --> 00:10:05.360
Like, yeah, they'll have a base 64 encoded string of something or other than they'll decode it and execute it.

00:10:05.360 --> 00:10:05.840
Right.

00:10:05.840 --> 00:10:06.640
And that's it.

00:10:06.640 --> 00:10:06.920
Yeah.

00:10:07.180 --> 00:10:12.320
So, yeah, there's created some outreach templates.

00:10:12.780 --> 00:10:23.320
So the full process, if you're confused or if you have a, this is something, if you get notified by an administrator that one of your packages is in quarantine, they'll
probably point you to this anyway.

00:10:23.320 --> 00:10:24.640
But, you know, check this out.

00:10:25.360 --> 00:10:34.080
I'm glad that they're working on this and we're making the environment easier for PIPI admins to deal with, but also just safer for everybody to use.

00:10:34.080 --> 00:10:35.060
So it's good.

00:10:35.060 --> 00:10:35.480
Yeah.

00:10:35.480 --> 00:10:35.940
Excellent.

00:10:35.940 --> 00:10:38.580
Well, you know, I'm sure you're aware of this, Brian.

00:10:38.580 --> 00:10:39.220
Testing.

00:10:39.220 --> 00:10:41.040
Testing makes your code safer to use.

00:10:41.040 --> 00:10:41.580
Yeah.

00:10:41.580 --> 00:10:45.600
And I have fully embraced the async lifestyle these days.

00:10:45.600 --> 00:10:51.260
You know, I talked about rewriting Python and Quart, the async version of Flask.

00:10:51.260 --> 00:10:54.220
And I blogged about that and brought it up on the show, I'm pretty sure.

00:10:54.540 --> 00:10:56.200
But how are you going to call APIs?

00:10:56.200 --> 00:10:59.820
I'm working on some projects right now that are like all about calling APIs.

00:10:59.820 --> 00:11:01.880
And I'm like, oh, my gosh, so many APIs.

00:11:01.880 --> 00:11:04.000
This thing calls that, which, you know, so on.

00:11:04.000 --> 00:11:06.820
If you can do that asynchronously, that'd be awesome.

00:11:06.820 --> 00:11:14.680
And I would say probably the best kind of, I'm a fan of requests, but I want async story these days is HTTPX.

00:11:14.680 --> 00:11:15.180
Yeah.

00:11:15.180 --> 00:11:24.020
Which has got some basically very, very similar, not identical, but very, very similar behaviors and API patterns as requests, but also has an async variant.

00:11:24.160 --> 00:11:27.560
Create the async client and then await all your calls, which is great.

00:11:27.560 --> 00:11:29.400
So you might want to test that, right?

00:11:29.400 --> 00:11:31.900
Even asynchronously as you're writing code.

00:11:31.900 --> 00:11:32.600
That's async.

00:11:32.600 --> 00:11:37.700
So I want to introduce people to RESPX, like response X.

00:11:37.700 --> 00:11:39.420
Probably is the way you pronounce that.

00:11:39.420 --> 00:11:42.160
I'm not talking about HTTPX or response X.

00:11:42.160 --> 00:11:42.500
I don't know.

00:11:42.500 --> 00:11:42.780
Whatever.

00:11:44.380 --> 00:11:44.820
HTTPX.

00:11:44.820 --> 00:11:49.920
And what it does is it lets you mock out HTTPX requests.

00:11:49.920 --> 00:11:51.420
Super, super easy.

00:11:51.420 --> 00:11:52.340
However you like.

00:11:52.340 --> 00:12:02.820
So for example, if I want to make a call where I say HTTPX get, and I want to make sure that if that URL comes in, it's going to return some particular value, like a 204.

00:12:03.060 --> 00:12:06.980
You just say RESP.same function call with the values.

00:12:06.980 --> 00:12:10.960
And then you just say .mock and you set the values or the behaviors that you want it to do.

00:12:10.960 --> 00:12:11.740
And off it goes.

00:12:11.740 --> 00:12:12.700
That's pretty cool.

00:12:12.700 --> 00:12:13.140
Yeah.

00:12:13.200 --> 00:12:16.840
And it also comes as a pytest plugin, if you want to roll that way.

00:12:16.840 --> 00:12:20.200
So then you just say RESP.mock.whatever and just call the functions.

00:12:20.200 --> 00:12:23.700
And then all the examples here, like first line, mock it.

00:12:23.700 --> 00:12:24.820
Second line, call it.

00:12:24.820 --> 00:12:31.560
But, you know, probably you're testing some function that then internally is using HTTPX through like a width, async width block.

00:12:31.560 --> 00:12:35.900
And, right, like there's a lot of layers going down there that you might need to work with.

00:12:35.900 --> 00:12:38.980
And so, right, that would be a more realistic example.

00:12:38.980 --> 00:12:42.680
You call the mock and then you call your code and then something happens, right?

00:12:42.840 --> 00:12:43.740
So that's pretty cool.

00:12:43.740 --> 00:12:45.500
You can even use mark.

00:12:45.500 --> 00:12:48.860
It probably makes sense of this pytest.mark statement here for me.

00:12:48.860 --> 00:12:49.520
What are we doing?

00:12:49.520 --> 00:12:52.400
Well, what do you mean?

00:12:52.400 --> 00:12:52.880
Okay.

00:12:52.880 --> 00:12:56.140
So you got pytest marking it with RESPX.

00:12:56.140 --> 00:13:03.080
So the project is to find a custom mark and it's passing in the base URL of foo.bar.

00:13:03.080 --> 00:13:04.600
And then within it.

00:13:04.600 --> 00:13:07.240
Yeah, you don't have to, I guess, say the base URL, right?

00:13:07.240 --> 00:13:07.740
Right.

00:13:07.740 --> 00:13:10.120
Because you're just passing it in.

00:13:10.280 --> 00:13:13.120
You can, because it's really, it's not that bad.

00:13:13.120 --> 00:13:18.040
Not that hard to pass in through markers of variable to fixtures.

00:13:18.040 --> 00:13:19.120
So that's what's going on.

00:13:19.120 --> 00:13:19.120
I see.

00:13:19.120 --> 00:13:21.380
So you kind of pre-pare it with your mark here.

00:13:21.380 --> 00:13:21.640
Okay.

00:13:21.640 --> 00:13:21.880
Yeah.

00:13:21.880 --> 00:13:22.080
Awesome.

00:13:22.080 --> 00:13:22.480
Yeah.

00:13:22.580 --> 00:13:24.280
And then the, then the fixtures passed in.

00:13:24.280 --> 00:13:24.740
Okay, cool.

00:13:24.740 --> 00:13:26.020
And that's pretty much it.

00:13:26.020 --> 00:13:28.240
There's not a whole lot of, not a lot of to say about it.

00:13:28.240 --> 00:13:32.220
But if you need to mock out HTTPX, instead of using generic mock stuff, you can use this

00:13:32.220 --> 00:13:36.080
library that basically has exactly the same API as HTTPX.

00:13:36.080 --> 00:13:36.600
Pretty cool.

00:13:37.000 --> 00:13:42.220
Sometimes I forget that not everybody has completely internalized the entire content of my book.

00:13:42.220 --> 00:13:44.420
Well, we can work on that.

00:13:44.420 --> 00:13:45.340
We can work on it.

00:13:45.340 --> 00:13:46.320
Yeah.

00:13:46.320 --> 00:13:49.400
I learned something new.

00:13:49.400 --> 00:13:50.580
Oh, really?

00:13:50.580 --> 00:13:51.500
You know what?

00:13:51.500 --> 00:13:55.320
If you, I think if this is your next topic, I had no idea about this either.

00:13:55.320 --> 00:13:56.760
So I'm about to learn something new.

00:13:57.000 --> 00:13:57.280
Okay.

00:13:57.280 --> 00:14:04.080
Well, so this is actually something that Rodrigo also learned something new because he marked

00:14:04.080 --> 00:14:06.800
it as a TIL for today I learned.

00:14:06.800 --> 00:14:13.680
And I kind of love people posting the TILs, but also I'm, I'm personally, personally somebody

00:14:13.680 --> 00:14:17.420
that I don't think you need to prefix things with TIL for today I learned.

00:14:17.420 --> 00:14:20.080
If you just have a small blog post, go ahead and post it.

00:14:20.080 --> 00:14:21.560
I like small posts.

00:14:21.560 --> 00:14:21.860
Anyway.

00:14:22.260 --> 00:14:26.960
So unpacking keyword args with, or K-Kwargs.

00:14:26.960 --> 00:14:29.040
I usually just say keyword args.

00:14:29.040 --> 00:14:30.720
Do you have, do you say quarks?

00:14:30.720 --> 00:14:32.480
I'm K-W-Args.

00:14:32.480 --> 00:14:33.460
K-W-Args.

00:14:33.460 --> 00:14:33.820
Okay.

00:14:33.820 --> 00:14:35.760
But I know people say quarks, but I don't know.

00:14:35.760 --> 00:14:37.620
It sounds like I'm speaking Klingon or something.

00:14:37.620 --> 00:14:38.140
I don't do it.

00:14:38.140 --> 00:14:38.660
Yeah.

00:14:38.660 --> 00:14:42.420
It makes me think of Deep Space Nine with quark.

00:14:42.420 --> 00:14:46.260
But unpacking keyword args with custom objects.

00:14:46.260 --> 00:14:49.340
So let's say you got, so there's a couple of things.

00:14:49.680 --> 00:14:56.380
And we're talking about the star or the double star or the splat splat or double splat.

00:14:56.380 --> 00:14:57.600
I don't really want to say it.

00:14:57.600 --> 00:15:04.380
So let's say you've got a dictionary and you want to pass that, the contents of the dictionary

00:15:04.380 --> 00:15:07.040
as arguments to a function or something.

00:15:07.380 --> 00:15:13.700
That's how we often use it, is doing a star star with a dictionary and it unpacks it into

00:15:13.700 --> 00:15:16.860
keyword args for a function call, which is cool.

00:15:16.860 --> 00:15:18.120
Or you can just do it.

00:15:18.120 --> 00:15:22.500
Here's an example of merging two dictionaries with this.

00:15:22.500 --> 00:15:23.080
I don't do it.

00:15:23.080 --> 00:15:25.300
I don't usually do this much, but cool.

00:15:25.300 --> 00:15:25.860
You can do that.

00:15:25.860 --> 00:15:29.640
There's a newer syntax where we use the pipe on dictionaries as well.

00:15:29.640 --> 00:15:30.600
And that's the same thing.

00:15:30.600 --> 00:15:33.540
There's like three or four ways to do this these days.

00:15:33.540 --> 00:15:38.100
Yeah, because with Python, there should be one obvious way to do.

00:15:38.100 --> 00:15:39.200
And if there's not, there's four.

00:15:39.200 --> 00:15:41.440
Unless it's strings, then there's six.

00:15:41.440 --> 00:15:43.980
Okay.

00:15:44.420 --> 00:15:51.840
So there's a lot of times where doing this star star unpacking is like so cool and convenient.

00:15:51.840 --> 00:15:58.160
And but if you have custom objects, not dictionaries, if you have your own objects, what do you want?

00:15:58.160 --> 00:15:59.640
How do you deal with that?

00:15:59.640 --> 00:16:00.440
Can you do that?

00:16:00.440 --> 00:16:01.340
Yes, you can.

00:16:01.340 --> 00:16:03.980
And that's what this little TIL is about.

00:16:03.980 --> 00:16:09.500
All you have to do is you have to add a keys function to your object or your class.

00:16:09.500 --> 00:16:14.160
And the keys function needs to or method needs to return an interval.

00:16:14.160 --> 00:16:16.960
And in this case, just a list is an interval, for instance.

00:16:16.960 --> 00:16:20.080
And and then the example, he's got a Harry Potter class.

00:16:20.080 --> 00:16:22.640
It was returning first, middle and last.

00:16:22.640 --> 00:16:30.180
And then a get item that presumably that takes a key and returns something.

00:16:30.180 --> 00:16:32.020
And that's all you need.

00:16:32.020 --> 00:16:35.120
And then you can you can do this double splat thing.

00:16:35.120 --> 00:16:36.140
And it works.

00:16:36.140 --> 00:16:37.240
Oh, that's awesome.

00:16:37.540 --> 00:16:48.700
And also the example is good also to just to remind everybody that when you're doing the get item to go ahead and do an else clause with a with a key error.

00:16:48.700 --> 00:16:52.480
So if people pass in the wrong thing, they get the appropriate exception.

00:16:52.480 --> 00:16:53.920
So anyway, thanks.

00:16:53.920 --> 00:16:55.220
Yeah, I love it.

00:16:55.220 --> 00:16:55.800
Very, very cool.

00:16:55.800 --> 00:16:57.440
All right.

00:16:57.440 --> 00:16:58.960
For items, right?

00:16:58.960 --> 00:17:00.260
That's it, I guess.

00:17:00.260 --> 00:17:02.120
You feel pretty extra.

00:17:02.120 --> 00:17:02.620
I can see.

00:17:02.620 --> 00:17:04.200
I do feel pretty extra.

00:17:04.200 --> 00:17:06.380
I got more extras than I had normal things.

00:17:06.380 --> 00:17:07.400
So let's jump in.

00:17:07.400 --> 00:17:08.360
So let's do it.

00:17:08.360 --> 00:17:11.960
Over on python test dot com.

00:17:11.960 --> 00:17:13.580
Oh, a couple of things.

00:17:13.580 --> 00:17:14.540
I'll just kind of go backwards.

00:17:14.540 --> 00:17:16.280
First off, I finally fixed it.

00:17:16.280 --> 00:17:20.160
I had X up or Twitter and I don't do Twitter anymore.

00:17:20.160 --> 00:17:23.060
So I replaced it with a blue sky icon.

00:17:23.060 --> 00:17:26.500
And also on my contact form has blue sky now.

00:17:26.500 --> 00:17:27.900
So I fixed those things.

00:17:29.040 --> 00:17:33.000
Also, I had like incorrect podcast stuff up.

00:17:33.000 --> 00:17:34.520
So I fixed my podcast data.

00:17:34.520 --> 00:17:36.640
Testing code and Python bytes and stuff.

00:17:36.640 --> 00:17:37.080
Of course.

00:17:37.080 --> 00:17:39.180
Anyway, that's not what I really want to talk about.

00:17:39.180 --> 00:17:41.900
What I want to talk about is the top pytest plugins.

00:17:42.600 --> 00:17:49.500
I've been researching a lot of the stuff in here for the testing code season two.

00:17:49.940 --> 00:17:54.040
And I'm relying on with this data.

00:17:54.040 --> 00:17:55.940
I'm relying on the top PyPI packages.

00:17:56.400 --> 00:17:59.400
And this is an excellent resource.

00:17:59.400 --> 00:18:01.320
And it uses BigQuery.

00:18:01.320 --> 00:18:07.300
And there was just a new article from the person that created this, Hugo.

00:18:08.300 --> 00:18:11.740
He wrote an article about what's going on with this.

00:18:11.740 --> 00:18:15.020
A surprising thing about PyPI is BigQuery data.

00:18:15.020 --> 00:18:18.280
And it's interesting and also kind of exciting news.

00:18:18.280 --> 00:18:29.180
So the interesting thing is he's using the free version of Google Big Cloud or BigQuery stuff.

00:18:29.180 --> 00:18:31.220
Whatever you need, a Google account.

00:18:31.220 --> 00:18:34.740
You get a few BigQuery queries.

00:18:34.740 --> 00:18:37.260
And if you do it too much, they kick you out.

00:18:38.060 --> 00:18:41.440
And so at first he started with 4,000 projects.

00:18:41.440 --> 00:18:43.400
Then he bumped up to 5,000 projects.

00:18:43.400 --> 00:18:45.080
And then 8,000 projects.

00:18:45.080 --> 00:18:46.580
But there's more than that.

00:18:46.580 --> 00:18:48.540
So he's like, well, I wonder how much I can do.

00:18:48.540 --> 00:18:51.840
And so this is a little test that he went through.

00:18:51.840 --> 00:18:54.760
I'm going to jump down to the punchline.

00:18:54.760 --> 00:18:58.480
And the punchline is that you can do.

00:18:58.480 --> 00:19:01.980
He went up to tried a million packages.

00:19:01.980 --> 00:19:04.060
And there aren't a million packages.

00:19:04.060 --> 00:19:08.160
But it returned 531,000 packages.

00:19:08.160 --> 00:19:14.660
And it was the same bytes processed as even just doing one for 30 days.

00:19:15.540 --> 00:19:18.500
So it doesn't matter.

00:19:18.500 --> 00:19:22.560
It turned out it didn't really matter how many packages to query.

00:19:22.560 --> 00:19:25.940
What it was was how the date time, the date spread.

00:19:26.300 --> 00:19:33.160
So if he did like five days, it was way cheaper than 15 days, which is way cheaper than 30 days.

00:19:33.160 --> 00:19:34.920
And it's relatively linear.

00:19:35.460 --> 00:19:43.140
So it looks like what he's going to do is change it so that we get like a ton of package data.

00:19:43.140 --> 00:19:44.620
Like as much as we can get.

00:19:44.620 --> 00:19:46.580
531,000.

00:19:46.580 --> 00:19:51.120
But he's probably going to report that in smaller chunks too.

00:19:51.120 --> 00:19:52.000
Because a lot of people are.

00:19:52.000 --> 00:19:52.820
Daily or something, yeah.

00:19:53.280 --> 00:19:57.140
But a lot of people aren't going to want to see 531,000.

00:19:57.140 --> 00:19:59.060
The top 8,000 is probably sufficient.

00:19:59.060 --> 00:20:00.800
You really got to zoom in to see them all at once.

00:20:00.800 --> 00:20:11.040
So I'm excited because when I'm using the 8,000 data set and the top pytest packages,

00:20:11.040 --> 00:20:15.200
there are currently 133 in the top 8,000.

00:20:15.200 --> 00:20:17.100
And I'd like to have a bigger list.

00:20:17.100 --> 00:20:23.780
So if I've got the top 10,000 or 20,000, I could probably get a bigger list of pytest packages.

00:20:23.780 --> 00:20:24.540
Anyway.

00:20:24.540 --> 00:20:27.420
So that's it.

00:20:27.420 --> 00:20:29.900
It's just an interesting thing if you're doing BigQuery data.

00:20:29.900 --> 00:20:34.400
It's the date that is the big effector of the price.

00:20:34.400 --> 00:20:34.640
Right.

00:20:34.640 --> 00:20:40.540
Because it probably counts the number of downloads for each day or per download individually.

00:20:40.540 --> 00:20:43.880
Whereas if there's only 500,000 packages, there's that.

00:20:43.880 --> 00:20:46.120
But there's way more downloads than there are packages.

00:20:46.560 --> 00:20:51.700
The other thing that might change is, I think is going to change,

00:20:51.700 --> 00:20:56.080
is that it cost more to filter on just pip packages.

00:20:56.080 --> 00:21:03.700
And now we're getting a lot of uv, people using uv to download stuff from PyPI.

00:21:03.700 --> 00:21:06.140
And so he wants to include that too.

00:21:06.140 --> 00:21:14.580
So it'll probably, I think he's going to change it so that the data is from everything instead of just PIPs.

00:21:14.580 --> 00:21:15.240
That makes sense.

00:21:15.240 --> 00:21:15.600
Yeah, it makes sense.

00:21:15.920 --> 00:21:16.580
Yeah, it definitely does.

00:21:16.580 --> 00:21:17.000
Yeah.

00:21:17.000 --> 00:21:17.440
Anyway.

00:21:17.440 --> 00:21:17.880
Awesome.

00:21:17.880 --> 00:21:18.320
Cool.

00:21:18.320 --> 00:21:19.080
Do you have any extras?

00:21:19.080 --> 00:21:19.800
I do.

00:21:19.800 --> 00:21:21.300
Not too many, but let's do it.

00:21:21.560 --> 00:21:25.440
So, oh, and I think it's going to be a good thing.

00:21:25.440 --> 00:21:29.900
Dash secure the project.

00:21:29.900 --> 00:21:30.900
I was speculating.

00:21:30.900 --> 00:21:33.540
I was speculating where, what API it was using.

00:21:33.540 --> 00:21:38.000
He wrote in to say, it's going to be a good thing.

00:21:38.000 --> 00:21:41.480
JSON API, JSON API at present to query for package vulnerability.

00:21:41.480 --> 00:21:42.720
Same thing that pip audit does.

00:21:42.720 --> 00:21:47.700
It does work at it asynchronously to try to make it a little faster, but it's just the simple API there.

00:21:47.700 --> 00:21:48.820
So that's what that is.

00:21:48.820 --> 00:21:53.000
Not something like sneak or some other more advanced threat modeling setup.

00:21:53.000 --> 00:21:53.920
And yeah, that's it.

00:21:53.920 --> 00:21:54.940
That's all I got for my extras.

00:21:55.360 --> 00:21:55.720
All right.

00:21:55.720 --> 00:21:56.140
Cool.

00:21:56.140 --> 00:21:56.740
How about a joke?

00:21:56.740 --> 00:21:57.880
Oh, I've got a joke.

00:21:57.880 --> 00:22:00.920
People, if they like puns and stuff, this is going to be good.

00:22:00.920 --> 00:22:05.820
It's at angle bracket slash angle bracket code puns or codepuns.com.

00:22:06.080 --> 00:22:10.880
So we've all written bad code and I know that sometimes testing will shake out the bugs, Brian,

00:22:10.880 --> 00:22:13.080
but do you know why programmers prefer dark mode?

00:22:13.080 --> 00:22:15.900
I think this is not totally wrong.

00:22:15.900 --> 00:22:17.200
I think we should switch it.

00:22:17.200 --> 00:22:19.260
I think it's a foul fallacy here.

00:22:19.260 --> 00:22:22.120
Why, I guess, I'll read it as it is.

00:22:22.120 --> 00:22:23.720
Why do programmers prefer dark mode?

00:22:23.720 --> 00:22:25.480
Because light attracts bugs.

00:22:25.480 --> 00:22:29.300
I guess if you're talking moths, but if you're talking cockroaches, it's the other way around.

00:22:29.300 --> 00:22:30.220
But here's the thing.

00:22:30.220 --> 00:22:34.780
That's a great joke, but you can click more puns and they just keep going.

00:22:34.780 --> 00:22:37.020
My love for coding is like a recursive function.

00:22:37.020 --> 00:22:38.140
This is not a very good one.

00:22:38.140 --> 00:22:38.620
That's fine.

00:22:38.620 --> 00:22:41.260
Why did the for loop stop running?

00:22:41.260 --> 00:22:42.760
It took a break, semicolon.

00:22:42.760 --> 00:22:46.300
How do you comfort a JavaScript bug?

00:22:46.300 --> 00:22:47.880
You console it.

00:22:47.880 --> 00:22:48.540
I see.

00:22:48.540 --> 00:22:49.640
There's a lot of good stuff here.

00:22:49.640 --> 00:22:53.060
Because console.log is how you debug that thing instead of bug.

00:22:53.060 --> 00:22:53.500
Oh, okay.

00:22:53.500 --> 00:22:53.780
Okay.

00:22:53.780 --> 00:22:57.320
It's the print debugging equivalent of JavaScript.

00:22:57.320 --> 00:22:58.720
I'm not a JavaScripter.

00:22:58.720 --> 00:22:58.980
Okay.

00:22:58.980 --> 00:23:01.840
Well, you certainly can't console your JavaScript bugs when you create them.

00:23:01.840 --> 00:23:02.360
All right.

00:23:02.360 --> 00:23:05.120
Why do you not want to function as a customer?

00:23:05.120 --> 00:23:07.040
Because they return a lot of items.

00:23:07.040 --> 00:23:07.720
Come on.

00:23:07.720 --> 00:23:14.580
Anyway, you can go to codepuns.com and click through until they can't take it anymore.

00:23:14.580 --> 00:23:15.400
Yeah.

00:23:15.400 --> 00:23:19.520
That's probably why you want to see customer because they only return one item.

00:23:19.520 --> 00:23:20.700
Oh, that's true, right?

00:23:20.700 --> 00:23:21.180
Yeah.

00:23:21.180 --> 00:23:24.800
Well, good stuff.

00:23:24.800 --> 00:23:25.360
Yeah.

00:23:25.360 --> 00:23:25.640
Good stuff.

00:23:25.640 --> 00:23:26.960
All right.

00:23:27.000 --> 00:23:27.780
Well, thanks again.

00:23:27.780 --> 00:23:30.860
Thanks for showing up for Python Bytes.

00:23:30.860 --> 00:23:32.600
And thanks, everybody, for listening.

00:23:32.600 --> 00:23:33.340
Yeah, you bet.

00:23:33.340 --> 00:23:34.200
Thanks for meeting you.

00:23:34.200 --> 00:23:34.660
Bye, everyone.

