WEBVTT

00:00:00.040 --> 00:00:04.480
Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to your earbuds.

00:00:04.860 --> 00:00:12.080
This is episode 441, recorded July 21st, 2025, and I am Brian Okken.

00:00:12.260 --> 00:00:14.460
Who am I? I am Michael Kennedy. Hello, everyone.

00:00:14.700 --> 00:00:17.560
This episode is sponsored by DigitalOcean. Thank you, DigitalOcean.

00:00:18.420 --> 00:00:19.620
Hear about them later in the show.

00:00:19.840 --> 00:00:24.320
If you'd like to give us some topics for the show or just ask us questions or connect,

00:00:24.420 --> 00:00:31.560
The links to Mastodon and Bluesky are in the show notes and on our webpage, pythonbytes.fm.

00:00:31.920 --> 00:00:35.700
While you're there, you may as well go ahead and sign up for our newsletter,

00:00:36.320 --> 00:00:39.700
or we'll send you links for the show afterwards.

00:00:40.720 --> 00:00:45.800
And I apologize, because last week was a little late, because I've been sick, but I am bitter now.

00:00:46.899 --> 00:00:52.940
And if you'd like to join the show, we do post it live, usually Tuesday mornings.

00:00:53.500 --> 00:00:56.700
Monday, no, Monday mornings, sorry, Monday mornings at 10.

00:00:57.090 --> 00:00:57.860
It used to be Tuesday.

00:00:59.420 --> 00:01:03.220
But just go ahead and go over to pythonbytes.fm/live

00:01:03.340 --> 00:01:04.920
and it'll tell you when the next one is.

00:01:05.740 --> 00:01:07.980
And then even if we change it, you can see there.

00:01:08.980 --> 00:01:10.320
Yeah, so let's kick it off.

00:01:10.330 --> 00:01:11.480
What do you got for us, Michael?

00:01:12.259 --> 00:01:15.000
Let's do some follow-up for our first item here.

00:01:15.260 --> 00:01:19.540
So remember, last week I talked about RQ Lite,

00:01:20.260 --> 00:01:25.000
the lightweight user-friendly distributed relational database built on SQLite.

00:01:25.380 --> 00:01:27.000
And that was pretty cool.

00:01:27.400 --> 00:01:31.800
You could build fault-tolerant relational databases in clusters,

00:01:32.500 --> 00:01:36.140
even potentially geo-distributed using SQLite.

00:01:36.600 --> 00:01:40.140
Well, as always, our wonderful listeners are like,

00:01:40.600 --> 00:01:41.060
that's cool.

00:01:41.280 --> 00:01:42.160
Did you know about?

00:01:42.960 --> 00:01:43.040
Right?

00:01:43.360 --> 00:01:43.540
Yeah.

00:01:43.760 --> 00:01:44.460
I love that though.

00:01:44.680 --> 00:01:44.820
Yeah.

00:01:45.020 --> 00:01:45.460
I do too.

00:01:45.580 --> 00:01:45.780
Yeah.

00:01:45.860 --> 00:01:46.200
Thank you.

00:01:46.860 --> 00:01:49.080
So two people wrote in and said,

00:01:49.180 --> 00:01:55.000
did you know about? And one was Michael Booth. And Michael Booth said, you should check out

00:01:55.280 --> 00:02:04.280
Terso, the next evolution of SQLite. So Terso is interesting in that it is kind of like SQLite,

00:02:04.560 --> 00:02:11.500
but rewritten in Rust with the idea that it can be a distributed database type of thing. Also,

00:02:12.160 --> 00:02:18.700
adding some of these AI features, as in vector search and other things that you need to be able

00:02:18.580 --> 00:02:20.380
to support working with AI.

00:02:20.920 --> 00:02:21.980
So pretty cool.

00:02:22.600 --> 00:02:23.920
If you look through the website,

00:02:24.240 --> 00:02:26.920
it's like their proper tercio.tech,

00:02:27.420 --> 00:02:29.320
which it's cool, but I don't know.

00:02:29.440 --> 00:02:32.520
It kind of leaves it a little bit unsure

00:02:33.280 --> 00:02:34.780
like what exactly it is.

00:02:34.840 --> 00:02:38.500
But like, what is this relation to SQLite, right?

00:02:38.960 --> 00:02:40.980
So it's, go over to their GitHub,

00:02:41.400 --> 00:02:42.900
it's a little bit clearer, right?

00:02:42.980 --> 00:02:44.600
So it has SQLite compatibility.

00:02:44.720 --> 00:02:48.660
It is not based on SQLite like RQ Lite is,

00:02:49.040 --> 00:02:51.220
but it has language biodeans for Python

00:02:52.020 --> 00:02:53.920
and other, maybe some of these other languages

00:02:54.220 --> 00:02:56.660
people have heard of like Java and JavaScript and so on.

00:02:56.940 --> 00:02:59.500
But it has asyncio support, which is really cool.

00:02:59.790 --> 00:03:02.020
And you might say, well, it's SQLite.

00:03:02.140 --> 00:03:03.440
Can it really have asyncio support?

00:03:04.040 --> 00:03:06.220
Yes, at least on Linux.

00:03:06.330 --> 00:03:08.980
It uses IO Uring to make that happen,

00:03:09.130 --> 00:03:11.480
but it runs on all the server or all the platforms.

00:03:11.730 --> 00:03:13.260
So you can like develop on macOS,

00:03:13.380 --> 00:03:16.120
but then you get better perf on Linux, for example.

00:03:16.440 --> 00:03:21.080
It also, they're working on adding a begin concurrent operation,

00:03:21.620 --> 00:03:24.680
like a SQL statement or query statement,

00:03:25.120 --> 00:03:26.620
and indexing for vector search,

00:03:26.880 --> 00:03:28.520
like probably pretty much required,

00:03:29.000 --> 00:03:30.420
and better alter support, right?

00:03:30.860 --> 00:03:35.380
So basically think of it as a more modern take,

00:03:35.980 --> 00:03:37.500
not just because it's rewritten in Rust,

00:03:37.600 --> 00:03:39.500
which seems to be like the thing you do,

00:03:39.880 --> 00:03:41.440
but as part of that,

00:03:41.500 --> 00:03:46.620
taking on a lot of ideas like concurrency others. Okay. Big question. Is it ready for production

00:03:46.780 --> 00:03:54.340
use? No, no. But these folks also worked on this thing called LibSQL. So it says that the

00:03:54.460 --> 00:03:59.600
Tarot database is the next evolution of SQLite and Rust. They had previously worked on something

00:03:59.720 --> 00:04:05.320
called LibSQL, which is an attempt to evolve SQLite in a similar direction, but through a fork

00:04:05.710 --> 00:04:10.280
rather than a rewrite. And they're like, we think this new database thing is a better way to go.

00:04:10.340 --> 00:04:15.680
So if what I talked about with RQ Lite inspires you, this also might.

00:04:15.940 --> 00:04:21.000
They also have some kind of what you might expect as open core,

00:04:21.579 --> 00:04:24.720
but we'll do the infrastructure side of things for you.

00:04:24.960 --> 00:04:26.620
So they've got this thing called Terso Cloud,

00:04:26.840 --> 00:04:30.160
which is a production or a paid sort of thing, right?

00:04:30.480 --> 00:04:34.320
So serverless access or sync or whatever you want, right?

00:04:34.460 --> 00:04:36.700
Replication, sync, vector search, all that kind of stuff.

00:04:36.880 --> 00:04:37.780
Basically managed.

00:04:38.220 --> 00:04:39.420
All right, so that's thing one.

00:04:39.900 --> 00:04:41.160
the other one comes to it.

00:04:41.180 --> 00:04:41.780
This is like all,

00:04:41.920 --> 00:04:42.720
it's all Michael.

00:04:42.900 --> 00:04:43.760
Michael's all the way down.

00:04:43.800 --> 00:04:45.440
So we had Michael Booth recommend that one.

00:04:45.800 --> 00:04:46.240
Got me,

00:04:46.380 --> 00:04:47.020
Michael presenting it.

00:04:47.020 --> 00:04:50.560
We got Mike Fiedler talking about the next one here.

00:04:51.040 --> 00:04:52.180
And Mike Fiedler says,

00:04:52.560 --> 00:04:53.340
have you heard,

00:04:53.500 --> 00:04:53.580
well,

00:04:53.880 --> 00:04:56.420
listen to the most recent Python bytes and RQ Lite.

00:04:56.620 --> 00:04:57.020
It's cool.

00:04:57.280 --> 00:04:57.760
Hadn't heard of that.

00:04:57.940 --> 00:05:01.220
Something related that might be interesting is lightstream.io.

00:05:01.700 --> 00:05:01.980
Okay.

00:05:02.060 --> 00:05:05.460
Now lightstream.io is more SQLite than before.

00:05:06.180 --> 00:05:09.020
You just keep using SQLite like the one built into Python.

00:05:09.580 --> 00:05:16.520
but you launch a little daemon that watches it and constantly syncs it with S3 or Azure Blob

00:05:16.640 --> 00:05:22.660
Storage or something like that. When you do that, what do you get? You get something pretty cool.

00:05:22.680 --> 00:05:29.860
It just constantly streams changes to AWS S3, Azure Blob Storage, Google Cloud Storage, etc.

00:05:30.480 --> 00:05:35.939
And that means you basically get failover. So imagine you're like, I would love to just have

00:05:35.960 --> 00:05:40.560
a SQLite database that I can run on a simple little app.

00:05:41.080 --> 00:05:42.940
But what if that server goes down?

00:05:43.270 --> 00:05:44.360
What if that server fails?

00:05:45.100 --> 00:05:50.180
Then all my data is lost from the last time I backed it up because my alternative was to

00:05:50.210 --> 00:05:54.440
run some kind of cluster of Postgres or Mongo or whatever, right?

00:05:54.510 --> 00:05:59.700
But then you're into lots of DevOps and managing those things, or you're paying for managed

00:06:00.120 --> 00:06:05.300
databases, which can be relatively expensive to small servers like DigitalOcean has.

00:06:06.320 --> 00:06:09.480
And what you can do is you can just keep on using regular SQLite,

00:06:09.860 --> 00:06:11.080
but then use this Lightstream,

00:06:11.190 --> 00:06:13.620
which has no real performance differences in terms,

00:06:14.090 --> 00:06:16.900
like these other two things are like trying to change how SQLite works

00:06:16.950 --> 00:06:18.940
or re-implement the compatible API.

00:06:19.400 --> 00:06:24.980
This one just says, we promise to hook into some,

00:06:25.320 --> 00:06:26.540
I put a link in the show notes.

00:06:26.720 --> 00:06:29.940
There's a nice write-up about this that they did.

00:06:30.060 --> 00:06:32.020
It's sort of like, why is this?

00:06:32.300 --> 00:06:35.060
And called Lightstream revamped.

00:06:35.360 --> 00:06:37.900
And it also just talks about like, why did they build this?

00:06:38.090 --> 00:06:38.700
How does it work?

00:06:38.840 --> 00:06:44.580
So it says it takes over the wall checkpointing process to continuously stream updates to

00:06:44.580 --> 00:06:44.960
the database.

00:06:45.320 --> 00:06:51.040
So basically as commits are committed to the SQLite file, it just keeps pushing that to

00:06:51.300 --> 00:06:51.700
blob storage.

00:06:52.140 --> 00:06:52.400
Cool, right?

00:06:52.740 --> 00:06:58.460
So if that RQ Lite thing was interesting, but not quite what you're looking for, maybe one

00:06:58.470 --> 00:06:59.700
of these two things is.

00:07:00.020 --> 00:07:00.280
Yeah.

00:07:00.560 --> 00:07:00.900
Interesting.

00:07:01.340 --> 00:07:01.740
Pennies a day.

00:07:01.770 --> 00:07:02.280
I like that.

00:07:02.540 --> 00:07:02.840
Dirt cheap.

00:07:03.140 --> 00:07:03.240
Yeah.

00:07:03.360 --> 00:07:03.620
Dirt cheap.

00:07:03.800 --> 00:07:07.720
only costs pennies a day because it's just blob storage, right?

00:07:07.910 --> 00:07:09.380
So however you do that.

00:07:09.600 --> 00:07:12.680
And the Lightstream, I don't know that they,

00:07:12.970 --> 00:07:13.680
I don't see pricing.

00:07:13.830 --> 00:07:14.300
How about that?

00:07:14.440 --> 00:07:16.160
So this is just pure open source,

00:07:16.490 --> 00:07:20.480
whereas the Terso was like,

00:07:20.780 --> 00:07:22.060
you can do the open source thing yourself

00:07:22.420 --> 00:07:23.960
or we can do our cloud thing, right?

00:07:24.180 --> 00:07:25.800
I don't know which of those makes you happier,

00:07:26.000 --> 00:07:27.460
but they're not the same in that regard.

00:07:27.600 --> 00:07:29.840
Or they are the same with a wrapper, pay wrapper.

00:07:30.100 --> 00:07:30.300
Anyway.

00:07:31.040 --> 00:07:31.120
Yeah.

00:07:32.140 --> 00:07:32.740
Okay, cool.

00:07:33.140 --> 00:07:33.320
Awesome.

00:07:33.560 --> 00:07:33.880
Over to you.

00:07:34.860 --> 00:07:37.280
I want to talk about project status a little bit.

00:07:37.680 --> 00:07:40.160
So I do want to talk about this new PEP.

00:07:40.410 --> 00:07:45.000
So PEP 792 project status markers in the simple index.

00:07:45.570 --> 00:07:48.200
So project status, what is this about?

00:07:48.480 --> 00:07:51.580
So this is a new PEP that was, well, it's not right, really that.

00:07:51.820 --> 00:07:52.700
It is pretty new.

00:07:53.650 --> 00:08:00.360
So it actually updates in February of this year, and it's now accepted.

00:08:00.740 --> 00:08:02.480
So I'm going to talk about this a little bit,

00:08:02.620 --> 00:08:05.140
but I want to talk about project status and what this means.

00:08:05.330 --> 00:08:08.080
So if I take a look at a couple of my silly projects,

00:08:08.840 --> 00:08:11.340
I've got pytestCheck, which is a project I maintain,

00:08:11.940 --> 00:08:14.680
and I actually intend people to be able to use it so it's maintained.

00:08:15.160 --> 00:08:17.860
And so I have added a Trove classifier

00:08:18.380 --> 00:08:21.100
to say the status of the project is production stable.

00:08:21.480 --> 00:08:24.280
I think you can use it in production, but it's, you know, yeah.

00:08:24.640 --> 00:08:25.780
But this is optional.

00:08:26.639 --> 00:08:28.220
Maintainers don't have to put this in.

00:08:28.450 --> 00:08:32.000
For instance, I also have another one called pytestCrayons

00:08:32.280 --> 00:08:39.219
i just did for a uh a talk that i gave and um i didn't put the um the status in there so that's

00:08:39.260 --> 00:08:44.200
an it's an optional thing so what we have we have in the current situation there is the trove

00:08:44.360 --> 00:08:52.040
classifiers that you can optionally add to um uh to to your project and you if you like for instance

00:08:52.620 --> 00:08:58.020
start taking it down you can say that it's inactive it's not but i don't think very many people do

00:08:58.000 --> 00:09:03.160
that often inactive just means somebody left but it's still they haven't updated a new one that's

00:09:03.220 --> 00:09:08.380
like please close the door on your way out usually they just walk out the door so and then there's

00:09:08.500 --> 00:09:13.340
also there's actually three kinds of status so there's that the trove classifiers but then there's

00:09:14.300 --> 00:09:22.440
also uh indices uh can mark uh a district can mark distributions and releases as yanked so you can

00:09:22.600 --> 00:09:24.680
like a version you can yank.

00:09:25.040 --> 00:09:26.300
So it's not there anymore.

00:09:26.900 --> 00:09:30.480
So then it's still like got an entry there,

00:09:30.610 --> 00:09:33.320
but it's been a yanked, whether it's true or false.

00:09:34.420 --> 00:09:37.500
You know what, I've started seeing that a lot lately, actually.

00:09:38.320 --> 00:09:38.600
Have you?

00:09:38.900 --> 00:09:40.720
At least uv pip warnings.

00:09:41.340 --> 00:09:44.280
So I'll say, you know, update the dependencies of this project,

00:09:44.540 --> 00:09:47.820
basically uv pip compile, and then I'll install the requirements.

00:09:48.350 --> 00:09:51.200
And it'll say, this thing has been yanked because of,

00:09:51.380 --> 00:09:54.520
and you don't have some reason, like broken metadata or whatever.

00:09:54.660 --> 00:09:56.660
I'm like, well, I don't know what I'm supposed to do about this

00:09:56.800 --> 00:09:57.600
because something needed it.

00:09:58.000 --> 00:09:58.220
Yeah.

00:09:59.160 --> 00:09:59.340
Okay.

00:09:59.860 --> 00:10:01.220
I've not seen anything fail.

00:10:01.340 --> 00:10:04.480
I haven't seen the apps no longer run because something got that status,

00:10:04.580 --> 00:10:06.100
but I've seen warnings about this now.

00:10:06.360 --> 00:10:11.200
And then there's another type of status that PyPI itself

00:10:11.860 --> 00:10:13.720
can have a status for an entire project.

00:10:14.240 --> 00:10:16.880
And the entire project can be quarantined

00:10:16.900 --> 00:10:21.440
if the administrators of PyPI think that the project has been,

00:10:22.710 --> 00:10:25.200
like, it's got, like, malware in it or something,

00:10:25.810 --> 00:10:26.980
so they can quarantine it.

00:10:27.380 --> 00:10:30.720
And also, a project owner can archive a project

00:10:30.910 --> 00:10:34.600
and say, basically, you can disable new releases.

00:10:35.860 --> 00:10:39.820
It's still around, but nobody can update to it,

00:10:40.020 --> 00:10:41.000
and it's just archived.

00:10:41.160 --> 00:10:43.300
It's there for historical purposes, I guess.

00:10:43.780 --> 00:10:45.380
Anyway, so there's three statuses,

00:10:45.420 --> 00:10:55.600
And it's a little confusing on how to use those as a as a if you had an alternative index, if you were trying to like do dependency resolution or other things.

00:10:56.400 --> 00:10:58.300
So trying to clean that up a bit.

00:10:58.340 --> 00:11:07.840
So there's really this proposals to have one project always has exactly one status and the status will be it'll be active.

00:11:08.800 --> 00:11:14.540
And then there's some semantics around that active or archived or quarantined.

00:11:15.280 --> 00:11:21.560
and deprecated and this makes sense so it's either either it's it's in use it's active or

00:11:22.100 --> 00:11:27.900
it's not um but it's also what does it not mean it's either you know there's various levels of

00:11:28.020 --> 00:11:33.540
why you can't you shouldn't use it um and i think this is um this is a way for good way forward to

00:11:33.720 --> 00:11:40.059
have like to kind of consolidate these now this is this has been accepted this uh this active

00:11:40.080 --> 00:11:42.740
archived, quarantined, deprecated status,

00:11:43.210 --> 00:11:44.500
and it can only be one of those.

00:11:45.500 --> 00:11:47.240
But this has been accepted.

00:11:47.450 --> 00:11:49.820
However, it's not implemented yet.

00:11:49.990 --> 00:11:52.260
So don't go out and look in PyPI

00:11:52.290 --> 00:11:53.440
to try to find this yet.

00:11:53.600 --> 00:11:57.540
So this just was accepted July 8th resolution.

00:11:58.100 --> 00:12:01.080
So we will update you further

00:12:01.350 --> 00:12:04.500
as we hear from people in the Python community

00:12:04.590 --> 00:12:05.920
that this is all implemented.

00:12:06.220 --> 00:12:06.960
Yeah, excellent.

00:12:07.320 --> 00:12:09.420
I think I'm behind it.

00:12:09.940 --> 00:12:11.180
The quarantine one is good.

00:12:11.240 --> 00:12:12.740
I'm very glad I've never seen that warning.

00:12:13.720 --> 00:12:19.540
And the archive one feels to me like a little bit of a supply chain safety type thing.

00:12:19.720 --> 00:12:20.840
Like, I'm not going to mess with this.

00:12:20.880 --> 00:12:22.140
I will never update this.

00:12:22.620 --> 00:12:25.700
But let's not allow something to happen where someone else could either.

00:12:25.940 --> 00:12:27.180
Well, and I'm not sure.

00:12:27.280 --> 00:12:28.460
So I was trying to read this.

00:12:28.480 --> 00:12:31.240
I was trying to understand kind of how it's going to be set.

00:12:31.480 --> 00:12:35.780
So it's probably not going to be something just in the Trove classifier.

00:12:36.000 --> 00:12:40.120
So it probably is going to be something that outside of the data itself.

00:12:40.880 --> 00:12:46.480
So because there's times where it's clear there's nobody updating it anymore

00:12:46.910 --> 00:12:48.580
and you can't get a hold of the maintainer,

00:12:48.790 --> 00:12:51.160
you need to be able to say, this one's dead.

00:12:52.400 --> 00:12:55.620
I think it's too bad that we don't have a better way to say,

00:12:56.860 --> 00:13:00.060
but after the fact, if somebody just stops maintaining something,

00:13:00.580 --> 00:13:02.600
hey, does anybody else want to start maintaining this?

00:13:02.900 --> 00:13:05.640
I know there's security problems around that,

00:13:05.860 --> 00:13:09.340
But, you know, it might be good to be able to have some things live on.

00:13:09.620 --> 00:13:12.520
But yeah, and just archiving old stuff.

00:13:12.950 --> 00:13:13.520
It's not good.

00:13:13.810 --> 00:13:14.520
Was it GitHub?

00:13:15.030 --> 00:13:25.920
I think that got into a big uproar because they decided that they're going to archive a bunch of stuff if it hadn't been updated in like years.

00:13:26.170 --> 00:13:27.120
Or was it NPM?

00:13:27.270 --> 00:13:27.540
I don't know.

00:13:27.660 --> 00:13:33.040
One of these types of places was like, if stuff doesn't get touched, people are like, hey, it's not unmaintained.

00:13:33.120 --> 00:13:33.640
It's just done.

00:13:33.920 --> 00:13:34.720
There's nothing to add.

00:13:34.800 --> 00:13:35.400
It's perfect.

00:13:35.420 --> 00:13:42.660
works. Yeah. Exactly. Cool. All right. Well, before we move on, let's talk about our sponsor,

00:13:42.760 --> 00:13:49.460
huh? Yeah. Yeah. So super excited to have DigitalOcean back to support the show. As always,

00:13:50.000 --> 00:13:54.820
this episode is brought to you by DigitalOcean. And DigitalOcean is a comprehensive cloud

00:13:55.200 --> 00:14:00.640
infrastructure that's simple to spin up even for the most complex workloads. And it's way better

00:14:00.920 --> 00:14:05.060
value than most cloud providers. I've looked at the big three and they're not even close.

00:14:05.580 --> 00:14:08.700
in how much value you get per dollar

00:14:08.810 --> 00:14:10.260
and how much easier it is to use.

00:14:10.470 --> 00:14:13.020
So at DigitalOcean, companies can save up to 30%

00:14:13.460 --> 00:14:14.360
off their cloud bill.

00:14:14.710 --> 00:14:18.640
They boast a 99.99% uptime SLA.

00:14:18.820 --> 00:14:22.960
That means they promise to support that level of uptime.

00:14:23.320 --> 00:14:25.380
Our experience, Brian, running Python Bytes

00:14:25.380 --> 00:14:27.500
and other things on DigitalOcean for many years

00:14:27.680 --> 00:14:29.600
was way higher than four nines.

00:14:29.940 --> 00:14:30.880
Really, really reliable.

00:14:31.380 --> 00:14:31.520
Love them.

00:14:31.900 --> 00:14:34.620
They also have industry-leading pricing on bandwidth.

00:14:35.140 --> 00:14:39.460
Also true, like 10 times, 8 times cheaper than AWS and Azure.

00:14:39.600 --> 00:14:39.860
Really good.

00:14:40.660 --> 00:14:44.440
So it's built to be the cloud backbone of businesses, small and large.

00:14:44.620 --> 00:14:51.200
And now they have GPU-powered virtual machines, plus storage, database, networking capabilities, all-in-one platform.

00:14:51.440 --> 00:14:55.720
So AI developers can confidently create apps that their users will love.

00:14:56.020 --> 00:15:02.860
And devs have access to a complete set of infrastructure tools they need, both training and inference, so they can build anything they dream up.

00:15:03.140 --> 00:15:05.840
DigitalOcean provides full-service cloud infrastructure that's simple to use,

00:15:06.260 --> 00:15:09.880
reliable no matter the use case, and scalable for any business.

00:15:10.180 --> 00:15:13.140
So did I say good value? $4 a month for a server.

00:15:13.800 --> 00:15:16.400
That's yours. Your Linux server, USSH, too. Amazing.

00:15:16.840 --> 00:15:19.780
And GPUs, servers for under $1 per hour.

00:15:20.280 --> 00:15:25.560
So easy to spin up infrastructure to simplify even the most intense business demands.

00:15:25.760 --> 00:15:26.340
That's DigitalOcean.

00:15:26.780 --> 00:15:35.220
If you use the DO4bytes code, then you can get up to $200 in free credit to get started.

00:15:36.200 --> 00:15:36.680
DO4bytes.

00:15:36.960 --> 00:15:39.400
Just click the link in your podcast player show notes.

00:15:39.700 --> 00:15:41.320
DigitalOcean is the cloud that's got you covered.

00:15:41.660 --> 00:15:43.200
So like I said, please use our link.

00:15:43.240 --> 00:15:47.780
You'll find the podcast player show notes, a clickable chapter URL while I'm speaking right now.

00:15:48.240 --> 00:15:51.560
And at the top of the page at pythonbytes.fm for the episode.

00:15:51.900 --> 00:15:54.960
Thank you to DigitalOcean for supporting Python Bytes.

00:15:55.220 --> 00:15:55.500
Indeed, indeed.

00:15:55.860 --> 00:15:57.360
Now, over to you.

00:15:57.640 --> 00:16:02.620
Well, so I want to talk about testing a little bit.

00:16:03.020 --> 00:16:07.400
So Hugo van Kaminade,

00:16:08.980 --> 00:16:10.520
love you, Hugo, sorry,

00:16:11.080 --> 00:16:13.700
wrote a post called Run Coverage on Tests.

00:16:13.720 --> 00:16:15.820
And this is something that I've taught everybody to do

00:16:16.840 --> 00:16:21.540
or taught everybody I can get hold of their ears to do this

00:16:21.920 --> 00:16:22.700
because it's important.

00:16:23.060 --> 00:16:25.180
But I'm glad that Hugo wrote a post about it.

00:16:25.260 --> 00:16:26.620
And this was just going to be an extra.

00:16:27.180 --> 00:16:30.240
However, I was blown away by a few things here.

00:16:30.580 --> 00:16:33.600
And I'm like, oh, we got to make this highlighted a little bit more.

00:16:34.180 --> 00:16:37.280
So the classic reason why you should run coverage on your test

00:16:37.560 --> 00:16:41.700
is because of the copy-paste-modify problem with pytest.

00:16:41.980 --> 00:16:45.580
Because pytest really kind of, the name of the test

00:16:45.680 --> 00:16:47.400
is just sort of shows up in the reporting.

00:16:47.700 --> 00:16:52.980
So it's very easy to copy my, you take an old test.

00:16:53.400 --> 00:16:55.720
And since you're not calling the test function yourself,

00:16:57.120 --> 00:16:59.280
it's easy to copy, paste, modify,

00:16:59.760 --> 00:17:01.120
and forget to change the name.

00:17:01.320 --> 00:17:02.260
And if you do that,

00:17:02.960 --> 00:17:05.319
the second test just hides the name,

00:17:05.459 --> 00:17:06.620
it hides the first one.

00:17:08.020 --> 00:17:09.380
And that is the classic reason

00:17:09.400 --> 00:17:11.260
why I tell people to run coverage

00:17:11.540 --> 00:17:12.540
so that you don't do this.

00:17:12.880 --> 00:17:14.959
And it's hard to figure out otherwise.

00:17:16.020 --> 00:17:17.100
But a couple of cool things

00:17:17.420 --> 00:17:19.319
that I learned from this post

00:17:19.579 --> 00:17:22.939
is that Ruff's rule F811,

00:17:23.240 --> 00:17:32.680
f811 catches that apparently so there is if you run ruff on your test code also with at least f811

00:17:32.940 --> 00:17:40.280
turned on you'll it it checks for variables which the function name is officially a variable

00:17:41.260 --> 00:17:49.200
that are defined and and redefined or otherwise shadowed and unused so if you used a variable

00:17:49.180 --> 00:17:55.780
didn't use it and redefined it um that's just that's similar to defining a test twice so um i

00:17:55.880 --> 00:18:01.020
didn't know that so i will make sure that i've got rough running on my test code also so that's cool

00:18:01.300 --> 00:18:06.260
also a tip to say hey if you're really just copy paste modifying a test maybe think about py test

00:18:06.440 --> 00:18:11.060
parameterization which i agree um you might want to just parameterize the test but this is also

00:18:11.240 --> 00:18:17.180
pretty common it's so common to copy paste the second example is what the one that i really was

00:18:17.200 --> 00:18:21.460
like oh my gosh we have to cover this because i would have never i wouldn't have caught this so

00:18:21.480 --> 00:18:28.820
his second example is this weird okay so it's like a he's got an image some image stuff that he's

00:18:29.000 --> 00:18:35.420
testing and it's like i don't know it's i guess i'm just it's a little bit of a complicated thing

00:18:35.740 --> 00:18:40.480
you've got some colors and images and you're trying to test in the in the end you're asserting

00:18:40.740 --> 00:18:46.480
whether or not images are similar of a reloaded image versus an expected image and i don't really

00:18:46.420 --> 00:18:52.760
understand what's going on here but um this isn't terrible but one of the things in here is that um

00:18:53.160 --> 00:18:59.040
when he ran coverage it said that uh these two lines at the bottom are not being run at all and

00:18:59.180 --> 00:19:06.200
he's got a a some images a set of images that he's iterating over and apparently there's nothing in

00:19:06.200 --> 00:19:15.020
them so what's going on um and the thing that's going on apparently is uh this his his images are

00:19:15.040 --> 00:19:22.060
a generator is what's going on so he's got some images that are being uh set up as a generator so

00:19:22.160 --> 00:19:28.900
like a uh you know for i in a generator comprehension yeah generator comprehension

00:19:29.250 --> 00:19:36.200
and then he passes that to a function that consumes the generator um and then uh and then when he tries

00:19:36.250 --> 00:19:42.220
to use it as the as in the assert section there's nothing there anymore and i would have never like

00:19:42.420 --> 00:19:47.780
obviously this is this is highlighted because i use generated way more than in generator

00:19:48.120 --> 00:19:53.220
comprehensions more than i used to um because they're cool um and they're efficient and if

00:19:53.280 --> 00:19:58.840
you've got huge things they use up less memory so i'm using them more and more and i probably am

00:19:58.920 --> 00:20:05.280
using in my test i better make sure that i'm not like mucking up with things like this um so uh

00:20:05.810 --> 00:20:11.419
pretty cool reason to use um uh use coverage on your test code to make sure that you don't do this

00:20:11.740 --> 00:20:19.820
Oh, yeah. The fix, by the way, of doing this is just instead of using a generator comprehension, just use a list and pass it to a desk.

00:20:19.890 --> 00:20:20.960
Square brackets, not parentheses.

00:20:21.590 --> 00:20:21.700
Yeah.

00:20:22.180 --> 00:20:22.680
Problem solved.

00:20:23.880 --> 00:20:25.580
It's subtle, though. I agree. Very subtle.

00:20:25.820 --> 00:20:30.660
Oh, yeah. That's the difference, really? Yeah. A parentheses for those brackets, and then it works.

00:20:31.740 --> 00:20:32.160
Well, yeah.

00:20:32.450 --> 00:20:34.220
Okay. Anyway, thanks, Hugo.

00:20:34.640 --> 00:20:35.400
Yeah. Thanks, Hugo.

00:20:35.880 --> 00:20:43.900
All right, let's talk about something simpler than all that SQLite distributed async business.

00:20:44.340 --> 00:20:45.060
Here's a problem, Brian.

00:20:45.490 --> 00:20:52.780
You have a program, probably in our case, often a Python program, and you want to give it to somebody and let them run it.

00:20:52.990 --> 00:20:53.860
How do you do that?

00:20:54.400 --> 00:20:58.620
Still, to this day, there's no amazing options to do that.

00:20:58.660 --> 00:21:07.860
I know we have Py2.exe and Py2.app, but those things are, while awesome, they're not consistently

00:21:08.420 --> 00:21:10.100
reliable and all sorts of things.

00:21:10.300 --> 00:21:13.180
So this is a little bit more dev-focused than that.

00:21:13.180 --> 00:21:17.020
But if you have Docker installed on the machine, right?

00:21:17.140 --> 00:21:24.300
So if Docker is like Docker Desktop or OrbStack or whatever, and I guess even in the upcoming

00:21:24.320 --> 00:21:30.040
macOS. We're supposed to have a built-in Docker equivalent built straight into macOS,

00:21:30.240 --> 00:21:34.060
which should be interesting. Anyway, if you have any of those, there's this project called Docker

00:21:34.420 --> 00:21:39.000
to EXE. Too executable. I believe it works on all the platforms. I don't think it's just

00:21:39.460 --> 00:21:44.400
Windows EXE, even though, you know, yeah, it absolutely works on Windows, Linux, and Darwin,

00:21:44.780 --> 00:21:50.940
aka macOS. So just too executable, right? And the idea is that if you have a Docker image

00:21:51.260 --> 00:21:54.380
with an entry point or something like that that will start an app running,

00:21:55.080 --> 00:22:02.140
you can simply build that as an executable, either a.exe on Windows, a.app.

00:22:02.350 --> 00:22:04.280
You have to rename it, I think, more or less.

00:22:04.560 --> 00:22:05.940
They're just executable binaries, right?

00:22:06.380 --> 00:22:10.340
So you can take that Docker image and build it into an executable,

00:22:10.470 --> 00:22:11.540
which is pretty awesome.

00:22:12.160 --> 00:22:15.720
So you could, you know, the example they give is kind of, it's a bad,

00:22:15.920 --> 00:22:18.840
I'm going to call it as a full-on bad example because it says,

00:22:18.920 --> 00:22:21.360
here is a bare Linux image.

00:22:21.860 --> 00:22:23.340
You can distribute that as a binary.

00:22:23.420 --> 00:22:24.380
It's like, okay, great.

00:22:25.500 --> 00:22:26.880
That's not exactly the use case.

00:22:27.300 --> 00:22:29.120
You know, the use case is I have something

00:22:29.540 --> 00:22:31.780
I want to give to someone and run it, right?

00:22:32.080 --> 00:22:33.560
So as a Docker thing,

00:22:33.620 --> 00:22:35.180
so maybe a better example would be like,

00:22:35.380 --> 00:22:36.820
here's copying over my source code,

00:22:37.140 --> 00:22:38.280
building a Docker image,

00:22:38.480 --> 00:22:40.300
and then turning that base Docker image

00:22:40.400 --> 00:22:42.620
into an executable that I can distribute, right?

00:22:42.860 --> 00:22:44.160
But this thing would do that.

00:22:44.300 --> 00:22:46.040
It's just the example doesn't show that.

00:22:46.340 --> 00:22:47.980
So I think that is pretty neat.

00:22:48.280 --> 00:22:57.180
And if you want yet another way to distribute a more durable tool to somebody, if they have Docker, then you're good to go.

00:22:57.540 --> 00:22:58.200
That's pretty cool.

00:22:58.580 --> 00:23:00.420
Well, do they have to have Docker?

00:23:00.840 --> 00:23:01.040
Yes.

00:23:01.360 --> 00:23:01.560
Okay.

00:23:01.920 --> 00:23:11.240
Because basically somehow this thing just bundles up the Docker image and then just executes it using Docker in the same way that you would just say Docker run, etc.

00:23:11.800 --> 00:23:32.420
The other thing that's not clear to me is if there's a way to pass through mappings, for example, map port 8000 to 2722, whatever, you know what I mean? Or map this volume to this folder. Probably you can in the exit, like when you run the executable, can you pass that kind of stuff to it?

00:23:32.480 --> 00:23:35.240
Also, again, the reason I don't like this example here,

00:23:35.470 --> 00:23:36.940
boy, they just like bundle up Linux.

00:23:37.500 --> 00:23:41.060
In the Docker file itself, you can say expose this port

00:23:41.300 --> 00:23:44.240
and do other type of things like that in the Docker file.

00:23:44.670 --> 00:23:46.740
So you can bake in, like if you're saying,

00:23:47.160 --> 00:23:50.880
my thing is a little Flask app that runs using maybe SQLite for the database.

00:23:51.900 --> 00:23:54.540
It exposes this port so that you can then, you know,

00:23:54.580 --> 00:23:57.460
it could print out a thing like click here to like talk to the server

00:23:57.940 --> 00:24:00.360
and launches a URL, the web browser to like a local URL.

00:24:00.760 --> 00:24:04.860
So you can do that with, I'm pretty sure, with expose and other stuff in the Docker file.

00:24:05.240 --> 00:24:09.740
But yeah, anyway, you want to ship a Docker image as just an executable binary?

00:24:10.030 --> 00:24:10.600
Check this thing out.

00:24:10.880 --> 00:24:11.140
Cool.

00:24:11.830 --> 00:24:12.080
Very neat.

00:24:12.420 --> 00:24:12.800
All right.

00:24:12.980 --> 00:24:14.760
I know that you have extras and I don't.

00:24:15.120 --> 00:24:15.300
Okay.

00:24:15.680 --> 00:24:18.020
I've got some testing code news.

00:24:18.200 --> 00:24:19.800
So testing code has sort of been on pause.

00:24:19.970 --> 00:24:20.800
When was the last time?

00:24:20.880 --> 00:24:21.580
May 7th.

00:24:22.960 --> 00:24:23.940
It's July now.

00:24:24.440 --> 00:24:26.800
It's been on pause for a while because of life.

00:24:26.980 --> 00:24:29.060
But life has got a little bit of room in it now.

00:24:29.160 --> 00:24:31.120
So I'm going to do a little bit of testing code.

00:24:31.370 --> 00:24:32.200
So there is a,

00:24:32.620 --> 00:24:36.800
there's three episodes in the queue and hopefully more to come.

00:24:37.080 --> 00:24:37.520
So at least,

00:24:38.120 --> 00:24:42.120
and so this week there will be an episode and I'm pretty excited because the,

00:24:42.250 --> 00:24:46.100
this week's episode is covering pytest Django with Adam Johnson.

00:24:46.280 --> 00:24:48.220
And a lot of people have asked me about pytest Django.

00:24:48.700 --> 00:24:52.000
And since I am not a Django expert,

00:24:52.440 --> 00:24:52.580
I,

00:24:53.220 --> 00:24:54.100
and Adam is,

00:24:54.520 --> 00:24:56.160
Adam was willing to talk with me about it.

00:24:56.320 --> 00:24:56.960
And it's a,

00:24:57.340 --> 00:24:58.540
it's a really great discussion.

00:24:58.900 --> 00:25:00.680
So I'll be excited to get that out.

00:25:01.500 --> 00:25:02.880
I'll be excited to get that out this week.

00:25:03.130 --> 00:25:05.260
And then also I went ahead.

00:25:05.550 --> 00:25:11.240
So I'm using a hosting provider I'm using for this is Transistor FM.

00:25:11.490 --> 00:25:16.420
And Transistor has the ability to push things to YouTube, but it's a little bit wacky.

00:25:16.620 --> 00:25:18.760
So I have went ahead and turned that on.

00:25:19.260 --> 00:25:22.480
And so everything now is for testing code.

00:25:22.590 --> 00:25:23.580
Well, not everything yet.

00:25:24.340 --> 00:25:27.140
207 episodes are now up on YouTube.

00:25:27.840 --> 00:25:31.660
The catch is they sort of show up in random order.

00:25:32.780 --> 00:25:37.300
They push, I think, 80 episodes a day or something like that.

00:25:37.960 --> 00:25:38.840
They do a lot.

00:25:39.000 --> 00:25:42.820
So by the time you probably hear this,

00:25:42.900 --> 00:25:44.540
if you don't hear it on the first day,

00:25:45.360 --> 00:25:46.660
they'll probably be all up.

00:25:46.880 --> 00:25:49.780
But anyway, they're there, but they're just in weird order.

00:25:50.000 --> 00:25:52.140
That's a lot of content to be pushing up there.

00:25:52.300 --> 00:25:55.200
I suspect, though, after it sort of bulk uploads,

00:25:55.400 --> 00:25:58.500
Once it does, as you start publishing, it'll go in the right order.

00:25:58.720 --> 00:26:01.240
Yeah, well, it's because YouTube orders,

00:26:01.580 --> 00:26:06.340
I'm pretty sure YouTube orders in the upload order of when you uploaded it.

00:26:06.560 --> 00:26:08.560
And there's no way to backdate it to say,

00:26:08.880 --> 00:26:10.980
this was actually two years ago or something.

00:26:11.100 --> 00:26:14.400
Yeah, and probably Transistor just batch processes them.

00:26:14.500 --> 00:26:16.780
And the order in which they complete is the order in which they upload.

00:26:17.320 --> 00:26:23.220
Luckily, when I transitioned to Transistor, though,

00:26:23.560 --> 00:26:26.760
They do have a, it just uses your old post dates.

00:26:27.200 --> 00:26:30.080
So even though I transitioned like, I don't know,

00:26:30.160 --> 00:26:31.740
a couple of years ago or something like that,

00:26:32.620 --> 00:26:34.540
I still was able to, you know,

00:26:34.830 --> 00:26:37.700
like some of the early episodes still show up

00:26:37.980 --> 00:26:40.080
as like being happening in 2016,

00:26:40.670 --> 00:26:42.200
even though they got transferred later.

00:26:42.500 --> 00:26:44.080
But anyway, a lot of content.

00:26:44.390 --> 00:26:44.760
Good deal.

00:26:45.100 --> 00:26:46.180
So no extras for you?

00:26:46.580 --> 00:26:48.180
No, I'm not very extra.

00:26:48.590 --> 00:26:49.740
Just been working hard and stuff.

00:26:49.800 --> 00:26:51.020
I'll share, I'll have things to share eventually,

00:26:51.280 --> 00:26:52.640
but right now, no extras.

00:26:53.080 --> 00:26:54.620
I do have a joke I've brought for us.

00:26:54.620 --> 00:26:55.080
What do you think?

00:26:55.460 --> 00:26:55.920
Oh, yeah.

00:26:56.160 --> 00:26:56.440
Let's do a joke.

00:26:56.440 --> 00:26:56.860
Are you here for it?

00:26:57.160 --> 00:26:57.260
Yeah.

00:26:57.340 --> 00:26:57.540
Okay.

00:26:57.920 --> 00:27:01.840
So, Brian, what if programmers were doctors?

00:27:02.360 --> 00:27:03.140
You know, the old joke.

00:27:03.980 --> 00:27:05.180
Hey, doctor, my leg hurts.

00:27:05.900 --> 00:27:06.180
Well, stop.

00:27:07.520 --> 00:27:09.500
My leg hurts when I lift it like this.

00:27:09.500 --> 00:27:11.060
Well, stop lifting it like that, right?

00:27:11.300 --> 00:27:11.440
Yeah.

00:27:11.940 --> 00:27:15.420
There's a fun variation on that joke for programmers,

00:27:16.060 --> 00:27:19.400
and it's basically the doctor equivalent of it works on my machine.

00:27:20.080 --> 00:27:22.420
It says, doctor, my leg hurts.

00:27:22.700 --> 00:27:23.180
That's weird.

00:27:23.270 --> 00:27:24.880
I also have a leg and it doesn't hurt.

00:27:24.950 --> 00:27:26.200
The issue must be on your side.

00:27:29.760 --> 00:27:31.620
Yeah, that's definitely true.

00:27:32.160 --> 00:27:32.240
Yeah.

00:27:34.400 --> 00:27:34.900
I like it.

00:27:35.220 --> 00:27:36.080
That's dumb, but funny.

00:27:36.300 --> 00:27:36.700
I know.

00:27:36.860 --> 00:27:37.800
It's short and sweet.

00:27:39.140 --> 00:27:39.600
I like it.

00:27:39.800 --> 00:27:39.960
Cool.

00:27:40.360 --> 00:27:41.780
Well, thanks for the joke, Michael.

00:27:42.010 --> 00:27:44.440
And thanks, as always, for this wonderful episode.

00:27:45.160 --> 00:27:45.540
You bet.

00:27:46.400 --> 00:27:46.740
Bye, Brian.

00:27:46.920 --> 00:27:47.260
Bye, everyone.

00:27:47.300 --> 00:27:47.400
Bye.

