WEBVTT
00:00:00.040 -->
00:00:04.480Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to your earbuds.
00:00:04.860 -->
00:00:12.080This is episode 441, recorded July 21st, 2025, and I am Brian Okken.
00:00:12.260 -->
00:00:14.460Who am I? I am Michael Kennedy. Hello, everyone.
00:00:14.700 -->
00:00:17.560This episode is sponsored by DigitalOcean. Thank you, DigitalOcean.
00:00:18.420 -->
00:00:19.620Hear about them later in the show.
00:00:19.840 -->
00:00:24.320If 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.560The links to Mastodon and Bluesky are in the show notes and on our webpage, pythonbytes.fm.
00:00:31.920 -->
00:00:35.700While you're there, you may as well go ahead and sign up for our newsletter,
00:00:36.320 -->
00:00:39.700or we'll send you links for the show afterwards.
00:00:40.720 -->
00:00:45.800And 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.940And if you'd like to join the show, we do post it live, usually Tuesday mornings.
00:00:53.500 -->
00:00:56.700Monday, no, Monday mornings, sorry, Monday mornings at 10.
00:00:57.090 -->
00:00:57.860It used to be Tuesday.
00:00:59.420 -->
00:01:03.220But just go ahead and go over to pythonbytes.fm/live
00:01:03.340 -->
00:01:04.920and it'll tell you when the next one is.
00:01:05.740 -->
00:01:07.980And then even if we change it, you can see there.
00:01:08.980 -->
00:01:10.320Yeah, so let's kick it off.
00:01:10.330 -->
00:01:11.480What do you got for us, Michael?
00:01:12.259 -->
00:01:15.000Let's do some follow-up for our first item here.
00:01:15.260 -->
00:01:19.540So remember, last week I talked about RQ Lite,
00:01:20.260 -->
00:01:25.000the lightweight user-friendly distributed relational database built on SQLite.
00:01:25.380 -->
00:01:27.000And that was pretty cool.
00:01:27.400 -->
00:01:31.800You could build fault-tolerant relational databases in clusters,
00:01:32.500 -->
00:01:36.140even potentially geo-distributed using SQLite.
00:01:36.600 -->
00:01:40.140Well, as always, our wonderful listeners are like,
00:01:40.600 -->
00:01:41.060that's cool.
00:01:41.280 -->
00:01:42.160Did you know about?
00:01:42.960 -->
00:01:43.040Right?
00:01:43.360 -->
00:01:43.540Yeah.
00:01:43.760 -->
00:01:44.460I love that though.
00:01:44.680 -->
00:01:44.820Yeah.
00:01:45.020 -->
00:01:45.460I do too.
00:01:45.580 -->
00:01:45.780Yeah.
00:01:45.860 -->
00:01:46.200Thank you.
00:01:46.860 -->
00:01:49.080So two people wrote in and said,
00:01:49.180 -->
00:01:55.000did you know about? And one was Michael Booth. And Michael Booth said, you should check out
00:01:55.280 -->
00:02:04.280Terso, the next evolution of SQLite. So Terso is interesting in that it is kind of like SQLite,
00:02:04.560 -->
00:02:11.500but rewritten in Rust with the idea that it can be a distributed database type of thing. Also,
00:02:12.160 -->
00:02:18.700adding 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.380to support working with AI.
00:02:20.920 -->
00:02:21.980So pretty cool.
00:02:22.600 -->
00:02:23.920If you look through the website,
00:02:24.240 -->
00:02:26.920it's like their proper tercio.tech,
00:02:27.420 -->
00:02:29.320which it's cool, but I don't know.
00:02:29.440 -->
00:02:32.520It kind of leaves it a little bit unsure
00:02:33.280 -->
00:02:34.780like what exactly it is.
00:02:34.840 -->
00:02:38.500But like, what is this relation to SQLite, right?
00:02:38.960 -->
00:02:40.980So it's, go over to their GitHub,
00:02:41.400 -->
00:02:42.900it's a little bit clearer, right?
00:02:42.980 -->
00:02:44.600So it has SQLite compatibility.
00:02:44.720 -->
00:02:48.660It is not based on SQLite like RQ Lite is,
00:02:49.040 -->
00:02:51.220but it has language biodeans for Python
00:02:52.020 -->
00:02:53.920and other, maybe some of these other languages
00:02:54.220 -->
00:02:56.660people have heard of like Java and JavaScript and so on.
00:02:56.940 -->
00:02:59.500But it has asyncio support, which is really cool.
00:02:59.790 -->
00:03:02.020And you might say, well, it's SQLite.
00:03:02.140 -->
00:03:03.440Can it really have asyncio support?
00:03:04.040 -->
00:03:06.220Yes, at least on Linux.
00:03:06.330 -->
00:03:08.980It uses IO Uring to make that happen,
00:03:09.130 -->
00:03:11.480but it runs on all the server or all the platforms.
00:03:11.730 -->
00:03:13.260So you can like develop on macOS,
00:03:13.380 -->
00:03:16.120but then you get better perf on Linux, for example.
00:03:16.440 -->
00:03:21.080It also, they're working on adding a begin concurrent operation,
00:03:21.620 -->
00:03:24.680like a SQL statement or query statement,
00:03:25.120 -->
00:03:26.620and indexing for vector search,
00:03:26.880 -->
00:03:28.520like probably pretty much required,
00:03:29.000 -->
00:03:30.420and better alter support, right?
00:03:30.860 -->
00:03:35.380So basically think of it as a more modern take,
00:03:35.980 -->
00:03:37.500not just because it's rewritten in Rust,
00:03:37.600 -->
00:03:39.500which seems to be like the thing you do,
00:03:39.880 -->
00:03:41.440but as part of that,
00:03:41.500 -->
00:03:46.620taking on a lot of ideas like concurrency others. Okay. Big question. Is it ready for production
00:03:46.780 -->
00:03:54.340use? No, no. But these folks also worked on this thing called LibSQL. So it says that the
00:03:54.460 -->
00:03:59.600Tarot database is the next evolution of SQLite and Rust. They had previously worked on something
00:03:59.720 -->
00:04:05.320called LibSQL, which is an attempt to evolve SQLite in a similar direction, but through a fork
00:04:05.710 -->
00:04:10.280rather 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.680So if what I talked about with RQ Lite inspires you, this also might.
00:04:15.940 -->
00:04:21.000They also have some kind of what you might expect as open core,
00:04:21.579 -->
00:04:24.720but we'll do the infrastructure side of things for you.
00:04:24.960 -->
00:04:26.620So they've got this thing called Terso Cloud,
00:04:26.840 -->
00:04:30.160which is a production or a paid sort of thing, right?
00:04:30.480 -->
00:04:34.320So serverless access or sync or whatever you want, right?
00:04:34.460 -->
00:04:36.700Replication, sync, vector search, all that kind of stuff.
00:04:36.880 -->
00:04:37.780Basically managed.
00:04:38.220 -->
00:04:39.420All right, so that's thing one.
00:04:39.900 -->
00:04:41.160the other one comes to it.
00:04:41.180 -->
00:04:41.780This is like all,
00:04:41.920 -->
00:04:42.720it's all Michael.
00:04:42.900 -->
00:04:43.760Michael's all the way down.
00:04:43.800 -->
00:04:45.440So we had Michael Booth recommend that one.
00:04:45.800 -->
00:04:46.240Got me,
00:04:46.380 -->
00:04:47.020Michael presenting it.
00:04:47.020 -->
00:04:50.560We got Mike Fiedler talking about the next one here.
00:04:51.040 -->
00:04:52.180And Mike Fiedler says,
00:04:52.560 -->
00:04:53.340have you heard,
00:04:53.500 -->
00:04:53.580well,
00:04:53.880 -->
00:04:56.420listen to the most recent Python bytes and RQ Lite.
00:04:56.620 -->
00:04:57.020It's cool.
00:04:57.280 -->
00:04:57.760Hadn't heard of that.
00:04:57.940 -->
00:05:01.220Something related that might be interesting is lightstream.io.
00:05:01.700 -->
00:05:01.980Okay.
00:05:02.060 -->
00:05:05.460Now lightstream.io is more SQLite than before.
00:05:06.180 -->
00:05:09.020You just keep using SQLite like the one built into Python.
00:05:09.580 -->
00:05:16.520but you launch a little daemon that watches it and constantly syncs it with S3 or Azure Blob
00:05:16.640 -->
00:05:22.660Storage or something like that. When you do that, what do you get? You get something pretty cool.
00:05:22.680 -->
00:05:29.860It just constantly streams changes to AWS S3, Azure Blob Storage, Google Cloud Storage, etc.
00:05:30.480 -->
00:05:35.939And that means you basically get failover. So imagine you're like, I would love to just have
00:05:35.960 -->
00:05:40.560a SQLite database that I can run on a simple little app.
00:05:41.080 -->
00:05:42.940But what if that server goes down?
00:05:43.270 -->
00:05:44.360What if that server fails?
00:05:45.100 -->
00:05:50.180Then 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.440run some kind of cluster of Postgres or Mongo or whatever, right?
00:05:54.510 -->
00:05:59.700But then you're into lots of DevOps and managing those things, or you're paying for managed
00:06:00.120 -->
00:06:05.300databases, which can be relatively expensive to small servers like DigitalOcean has.
00:06:06.320 -->
00:06:09.480And what you can do is you can just keep on using regular SQLite,
00:06:09.860 -->
00:06:11.080but then use this Lightstream,
00:06:11.190 -->
00:06:13.620which has no real performance differences in terms,
00:06:14.090 -->
00:06:16.900like these other two things are like trying to change how SQLite works
00:06:16.950 -->
00:06:18.940or re-implement the compatible API.
00:06:19.400 -->
00:06:24.980This one just says, we promise to hook into some,
00:06:25.320 -->
00:06:26.540I put a link in the show notes.
00:06:26.720 -->
00:06:29.940There's a nice write-up about this that they did.
00:06:30.060 -->
00:06:32.020It's sort of like, why is this?
00:06:32.300 -->
00:06:35.060And called Lightstream revamped.
00:06:35.360 -->
00:06:37.900And it also just talks about like, why did they build this?
00:06:38.090 -->
00:06:38.700How does it work?
00:06:38.840 -->
00:06:44.580So it says it takes over the wall checkpointing process to continuously stream updates to
00:06:44.580 -->
00:06:44.960the database.
00:06:45.320 -->
00:06:51.040So basically as commits are committed to the SQLite file, it just keeps pushing that to
00:06:51.300 -->
00:06:51.700blob storage.
00:06:52.140 -->
00:06:52.400Cool, right?
00:06:52.740 -->
00:06:58.460So if that RQ Lite thing was interesting, but not quite what you're looking for, maybe one
00:06:58.470 -->
00:06:59.700of these two things is.
00:07:00.020 -->
00:07:00.280Yeah.
00:07:00.560 -->
00:07:00.900Interesting.
00:07:01.340 -->
00:07:01.740Pennies a day.
00:07:01.770 -->
00:07:02.280I like that.
00:07:02.540 -->
00:07:02.840Dirt cheap.
00:07:03.140 -->
00:07:03.240Yeah.
00:07:03.360 -->
00:07:03.620Dirt cheap.
00:07:03.800 -->
00:07:07.720only costs pennies a day because it's just blob storage, right?
00:07:07.910 -->
00:07:09.380So however you do that.
00:07:09.600 -->
00:07:12.680And the Lightstream, I don't know that they,
00:07:12.970 -->
00:07:13.680I don't see pricing.
00:07:13.830 -->
00:07:14.300How about that?
00:07:14.440 -->
00:07:16.160So this is just pure open source,
00:07:16.490 -->
00:07:20.480whereas the Terso was like,
00:07:20.780 -->
00:07:22.060you can do the open source thing yourself
00:07:22.420 -->
00:07:23.960or we can do our cloud thing, right?
00:07:24.180 -->
00:07:25.800I don't know which of those makes you happier,
00:07:26.000 -->
00:07:27.460but they're not the same in that regard.
00:07:27.600 -->
00:07:29.840Or they are the same with a wrapper, pay wrapper.
00:07:30.100 -->
00:07:30.300Anyway.
00:07:31.040 -->
00:07:31.120Yeah.
00:07:32.140 -->
00:07:32.740Okay, cool.
00:07:33.140 -->
00:07:33.320Awesome.
00:07:33.560 -->
00:07:33.880Over to you.
00:07:34.860 -->
00:07:37.280I want to talk about project status a little bit.
00:07:37.680 -->
00:07:40.160So I do want to talk about this new PEP.
00:07:40.410 -->
00:07:45.000So PEP 792 project status markers in the simple index.
00:07:45.570 -->
00:07:48.200So project status, what is this about?
00:07:48.480 -->
00:07:51.580So this is a new PEP that was, well, it's not right, really that.
00:07:51.820 -->
00:07:52.700It is pretty new.
00:07:53.650 -->
00:08:00.360So it actually updates in February of this year, and it's now accepted.
00:08:00.740 -->
00:08:02.480So I'm going to talk about this a little bit,
00:08:02.620 -->
00:08:05.140but I want to talk about project status and what this means.
00:08:05.330 -->
00:08:08.080So if I take a look at a couple of my silly projects,
00:08:08.840 -->
00:08:11.340I've got pytestCheck, which is a project I maintain,
00:08:11.940 -->
00:08:14.680and I actually intend people to be able to use it so it's maintained.
00:08:15.160 -->
00:08:17.860And so I have added a Trove classifier
00:08:18.380 -->
00:08:21.100to say the status of the project is production stable.
00:08:21.480 -->
00:08:24.280I think you can use it in production, but it's, you know, yeah.
00:08:24.640 -->
00:08:25.780But this is optional.
00:08:26.639 -->
00:08:28.220Maintainers don't have to put this in.
00:08:28.450 -->
00:08:32.000For instance, I also have another one called pytestCrayons
00:08:32.280 -->
00:08:39.219i 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.200an 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.040classifiers 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.020start 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.160that 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.380like 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.340also 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.440also 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.680like a version you can yank.
00:09:25.040 -->
00:09:26.300So it's not there anymore.
00:09:26.900 -->
00:09:30.480So then it's still like got an entry there,
00:09:30.610 -->
00:09:33.320but it's been a yanked, whether it's true or false.
00:09:34.420 -->
00:09:37.500You know what, I've started seeing that a lot lately, actually.
00:09:38.320 -->
00:09:38.600Have you?
00:09:38.900 -->
00:09:40.720At least uv pip warnings.
00:09:41.340 -->
00:09:44.280So I'll say, you know, update the dependencies of this project,
00:09:44.540 -->
00:09:47.820basically uv pip compile, and then I'll install the requirements.
00:09:48.350 -->
00:09:51.200And it'll say, this thing has been yanked because of,
00:09:51.380 -->
00:09:54.520and you don't have some reason, like broken metadata or whatever.
00:09:54.660 -->
00:09:56.660I'm like, well, I don't know what I'm supposed to do about this
00:09:56.800 -->
00:09:57.600because something needed it.
00:09:58.000 -->
00:09:58.220Yeah.
00:09:59.160 -->
00:09:59.340Okay.
00:09:59.860 -->
00:10:01.220I've not seen anything fail.
00:10:01.340 -->
00:10:04.480I haven't seen the apps no longer run because something got that status,
00:10:04.580 -->
00:10:06.100but I've seen warnings about this now.
00:10:06.360 -->
00:10:11.200And then there's another type of status that PyPI itself
00:10:11.860 -->
00:10:13.720can have a status for an entire project.
00:10:14.240 -->
00:10:16.880And the entire project can be quarantined
00:10:16.900 -->
00:10:21.440if the administrators of PyPI think that the project has been,
00:10:22.710 -->
00:10:25.200like, it's got, like, malware in it or something,
00:10:25.810 -->
00:10:26.980so they can quarantine it.
00:10:27.380 -->
00:10:30.720And also, a project owner can archive a project
00:10:30.910 -->
00:10:34.600and say, basically, you can disable new releases.
00:10:35.860 -->
00:10:39.820It's still around, but nobody can update to it,
00:10:40.020 -->
00:10:41.000and it's just archived.
00:10:41.160 -->
00:10:43.300It's there for historical purposes, I guess.
00:10:43.780 -->
00:10:45.380Anyway, so there's three statuses,
00:10:45.420 -->
00:10:55.600And 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.300So trying to clean that up a bit.
00:10:58.340 -->
00:11:07.840So 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.540And then there's some semantics around that active or archived or quarantined.
00:11:15.280 -->
00:11:21.560and 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.900it'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.540why 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.059have like to kind of consolidate these now this is this has been accepted this uh this active
00:11:40.080 -->
00:11:42.740archived, quarantined, deprecated status,
00:11:43.210 -->
00:11:44.500and it can only be one of those.
00:11:45.500 -->
00:11:47.240But this has been accepted.
00:11:47.450 -->
00:11:49.820However, it's not implemented yet.
00:11:49.990 -->
00:11:52.260So don't go out and look in PyPI
00:11:52.290 -->
00:11:53.440to try to find this yet.
00:11:53.600 -->
00:11:57.540So this just was accepted July 8th resolution.
00:11:58.100 -->
00:12:01.080So we will update you further
00:12:01.350 -->
00:12:04.500as we hear from people in the Python community
00:12:04.590 -->
00:12:05.920that this is all implemented.
00:12:06.220 -->
00:12:06.960Yeah, excellent.
00:12:07.320 -->
00:12:09.420I think I'm behind it.
00:12:09.940 -->
00:12:11.180The quarantine one is good.
00:12:11.240 -->
00:12:12.740I'm very glad I've never seen that warning.
00:12:13.720 -->
00:12:19.540And the archive one feels to me like a little bit of a supply chain safety type thing.
00:12:19.720 -->
00:12:20.840Like, I'm not going to mess with this.
00:12:20.880 -->
00:12:22.140I will never update this.
00:12:22.620 -->
00:12:25.700But let's not allow something to happen where someone else could either.
00:12:25.940 -->
00:12:27.180Well, and I'm not sure.
00:12:27.280 -->
00:12:28.460So I was trying to read this.
00:12:28.480 -->
00:12:31.240I was trying to understand kind of how it's going to be set.
00:12:31.480 -->
00:12:35.780So it's probably not going to be something just in the Trove classifier.
00:12:36.000 -->
00:12:40.120So it probably is going to be something that outside of the data itself.
00:12:40.880 -->
00:12:46.480So because there's times where it's clear there's nobody updating it anymore
00:12:46.910 -->
00:12:48.580and you can't get a hold of the maintainer,
00:12:48.790 -->
00:12:51.160you need to be able to say, this one's dead.
00:12:52.400 -->
00:12:55.620I think it's too bad that we don't have a better way to say,
00:12:56.860 -->
00:13:00.060but after the fact, if somebody just stops maintaining something,
00:13:00.580 -->
00:13:02.600hey, does anybody else want to start maintaining this?
00:13:02.900 -->
00:13:05.640I know there's security problems around that,
00:13:05.860 -->
00:13:09.340But, you know, it might be good to be able to have some things live on.
00:13:09.620 -->
00:13:12.520But yeah, and just archiving old stuff.
00:13:12.950 -->
00:13:13.520It's not good.
00:13:13.810 -->
00:13:14.520Was it GitHub?
00:13:15.030 -->
00:13:25.920I 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.120Or was it NPM?
00:13:27.270 -->
00:13:27.540I don't know.
00:13:27.660 -->
00:13:33.040One 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.640It's just done.
00:13:33.920 -->
00:13:34.720There's nothing to add.
00:13:34.800 -->
00:13:35.400It's perfect.
00:13:35.420 -->
00:13:42.660works. Yeah. Exactly. Cool. All right. Well, before we move on, let's talk about our sponsor,
00:13:42.760 -->
00:13:49.460huh? Yeah. Yeah. So super excited to have DigitalOcean back to support the show. As always,
00:13:50.000 -->
00:13:54.820this episode is brought to you by DigitalOcean. And DigitalOcean is a comprehensive cloud
00:13:55.200 -->
00:14:00.640infrastructure that's simple to spin up even for the most complex workloads. And it's way better
00:14:00.920 -->
00:14:05.060value than most cloud providers. I've looked at the big three and they're not even close.
00:14:05.580 -->
00:14:08.700in how much value you get per dollar
00:14:08.810 -->
00:14:10.260and how much easier it is to use.
00:14:10.470 -->
00:14:13.020So at DigitalOcean, companies can save up to 30%
00:14:13.460 -->
00:14:14.360off their cloud bill.
00:14:14.710 -->
00:14:18.640They boast a 99.99% uptime SLA.
00:14:18.820 -->
00:14:22.960That means they promise to support that level of uptime.
00:14:23.320 -->
00:14:25.380Our experience, Brian, running Python Bytes
00:14:25.380 -->
00:14:27.500and other things on DigitalOcean for many years
00:14:27.680 -->
00:14:29.600was way higher than four nines.
00:14:29.940 -->
00:14:30.880Really, really reliable.
00:14:31.380 -->
00:14:31.520Love them.
00:14:31.900 -->
00:14:34.620They also have industry-leading pricing on bandwidth.
00:14:35.140 -->
00:14:39.460Also true, like 10 times, 8 times cheaper than AWS and Azure.
00:14:39.600 -->
00:14:39.860Really good.
00:14:40.660 -->
00:14:44.440So it's built to be the cloud backbone of businesses, small and large.
00:14:44.620 -->
00:14:51.200And now they have GPU-powered virtual machines, plus storage, database, networking capabilities, all-in-one platform.
00:14:51.440 -->
00:14:55.720So AI developers can confidently create apps that their users will love.
00:14:56.020 -->
00:15:02.860And 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.840DigitalOcean provides full-service cloud infrastructure that's simple to use,
00:15:06.260 -->
00:15:09.880reliable no matter the use case, and scalable for any business.
00:15:10.180 -->
00:15:13.140So did I say good value? $4 a month for a server.
00:15:13.800 -->
00:15:16.400That's yours. Your Linux server, USSH, too. Amazing.
00:15:16.840 -->
00:15:19.780And GPUs, servers for under $1 per hour.
00:15:20.280 -->
00:15:25.560So easy to spin up infrastructure to simplify even the most intense business demands.
00:15:25.760 -->
00:15:26.340That's DigitalOcean.
00:15:26.780 -->
00:15:35.220If 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.680DO4bytes.
00:15:36.960 -->
00:15:39.400Just click the link in your podcast player show notes.
00:15:39.700 -->
00:15:41.320DigitalOcean is the cloud that's got you covered.
00:15:41.660 -->
00:15:43.200So like I said, please use our link.
00:15:43.240 -->
00:15:47.780You'll find the podcast player show notes, a clickable chapter URL while I'm speaking right now.
00:15:48.240 -->
00:15:51.560And at the top of the page at pythonbytes.fm for the episode.
00:15:51.900 -->
00:15:54.960Thank you to DigitalOcean for supporting Python Bytes.
00:15:55.220 -->
00:15:55.500Indeed, indeed.
00:15:55.860 -->
00:15:57.360Now, over to you.
00:15:57.640 -->
00:16:02.620Well, so I want to talk about testing a little bit.
00:16:03.020 -->
00:16:07.400So Hugo van Kaminade,
00:16:08.980 -->
00:16:10.520love you, Hugo, sorry,
00:16:11.080 -->
00:16:13.700wrote a post called Run Coverage on Tests.
00:16:13.720 -->
00:16:15.820And this is something that I've taught everybody to do
00:16:16.840 -->
00:16:21.540or taught everybody I can get hold of their ears to do this
00:16:21.920 -->
00:16:22.700because it's important.
00:16:23.060 -->
00:16:25.180But I'm glad that Hugo wrote a post about it.
00:16:25.260 -->
00:16:26.620And this was just going to be an extra.
00:16:27.180 -->
00:16:30.240However, I was blown away by a few things here.
00:16:30.580 -->
00:16:33.600And I'm like, oh, we got to make this highlighted a little bit more.
00:16:34.180 -->
00:16:37.280So the classic reason why you should run coverage on your test
00:16:37.560 -->
00:16:41.700is because of the copy-paste-modify problem with pytest.
00:16:41.980 -->
00:16:45.580Because pytest really kind of, the name of the test
00:16:45.680 -->
00:16:47.400is just sort of shows up in the reporting.
00:16:47.700 -->
00:16:52.980So it's very easy to copy my, you take an old test.
00:16:53.400 -->
00:16:55.720And since you're not calling the test function yourself,
00:16:57.120 -->
00:16:59.280it's easy to copy, paste, modify,
00:16:59.760 -->
00:17:01.120and forget to change the name.
00:17:01.320 -->
00:17:02.260And if you do that,
00:17:02.960 -->
00:17:05.319the second test just hides the name,
00:17:05.459 -->
00:17:06.620it hides the first one.
00:17:08.020 -->
00:17:09.380And that is the classic reason
00:17:09.400 -->
00:17:11.260why I tell people to run coverage
00:17:11.540 -->
00:17:12.540so that you don't do this.
00:17:12.880 -->
00:17:14.959And it's hard to figure out otherwise.
00:17:16.020 -->
00:17:17.100But a couple of cool things
00:17:17.420 -->
00:17:19.319that I learned from this post
00:17:19.579 -->
00:17:22.939is that Ruff's rule F811,
00:17:23.240 -->
00:17:32.680f811 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.280turned on you'll it it checks for variables which the function name is officially a variable
00:17:41.260 -->
00:17:49.200that are defined and and redefined or otherwise shadowed and unused so if you used a variable
00:17:49.180 -->
00:17:55.780didn'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.020didn'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.260also 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.060parameterization which i agree um you might want to just parameterize the test but this is also
00:18:11.240 -->
00:18:17.180pretty 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.460like 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.820his 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.420testing 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.480you'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.480whether 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.760understand 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.040when 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.200he'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.020them 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.060a 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.900like a uh you know for i in a generator comprehension yeah generator comprehension
00:19:29.250 -->
00:19:36.200and 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.220to 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.780obviously this is this is highlighted because i use generated way more than in generator
00:19:48.120 -->
00:19:53.220comprehensions more than i used to um because they're cool um and they're efficient and if
00:19:53.280 -->
00:19:58.840you'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.280using 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.419pretty 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.820Oh, 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.960Square brackets, not parentheses.
00:20:21.590 -->
00:20:21.700Yeah.
00:20:22.180 -->
00:20:22.680Problem solved.
00:20:23.880 -->
00:20:25.580It's subtle, though. I agree. Very subtle.
00:20:25.820 -->
00:20:30.660Oh, yeah. That's the difference, really? Yeah. A parentheses for those brackets, and then it works.
00:20:31.740 -->
00:20:32.160Well, yeah.
00:20:32.450 -->
00:20:34.220Okay. Anyway, thanks, Hugo.
00:20:34.640 -->
00:20:35.400Yeah. Thanks, Hugo.
00:20:35.880 -->
00:20:43.900All right, let's talk about something simpler than all that SQLite distributed async business.
00:20:44.340 -->
00:20:45.060Here's a problem, Brian.
00:20:45.490 -->
00:20:52.780You 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.860How do you do that?
00:20:54.400 -->
00:20:58.620Still, to this day, there's no amazing options to do that.
00:20:58.660 -->
00:21:07.860I 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.100reliable and all sorts of things.
00:21:10.300 -->
00:21:13.180So this is a little bit more dev-focused than that.
00:21:13.180 -->
00:21:17.020But if you have Docker installed on the machine, right?
00:21:17.140 -->
00:21:24.300So if Docker is like Docker Desktop or OrbStack or whatever, and I guess even in the upcoming
00:21:24.320 -->
00:21:30.040macOS. We're supposed to have a built-in Docker equivalent built straight into macOS,
00:21:30.240 -->
00:21:34.060which should be interesting. Anyway, if you have any of those, there's this project called Docker
00:21:34.420 -->
00:21:39.000to 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.400Windows EXE, even though, you know, yeah, it absolutely works on Windows, Linux, and Darwin,
00:21:44.780 -->
00:21:50.940aka macOS. So just too executable, right? And the idea is that if you have a Docker image
00:21:51.260 -->
00:21:54.380with an entry point or something like that that will start an app running,
00:21:55.080 -->
00:22:02.140you can simply build that as an executable, either a.exe on Windows, a.app.
00:22:02.350 -->
00:22:04.280You have to rename it, I think, more or less.
00:22:04.560 -->
00:22:05.940They're just executable binaries, right?
00:22:06.380 -->
00:22:10.340So you can take that Docker image and build it into an executable,
00:22:10.470 -->
00:22:11.540which is pretty awesome.
00:22:12.160 -->
00:22:15.720So you could, you know, the example they give is kind of, it's a bad,
00:22:15.920 -->
00:22:18.840I'm going to call it as a full-on bad example because it says,
00:22:18.920 -->
00:22:21.360here is a bare Linux image.
00:22:21.860 -->
00:22:23.340You can distribute that as a binary.
00:22:23.420 -->
00:22:24.380It's like, okay, great.
00:22:25.500 -->
00:22:26.880That's not exactly the use case.
00:22:27.300 -->
00:22:29.120You know, the use case is I have something
00:22:29.540 -->
00:22:31.780I want to give to someone and run it, right?
00:22:32.080 -->
00:22:33.560So as a Docker thing,
00:22:33.620 -->
00:22:35.180so maybe a better example would be like,
00:22:35.380 -->
00:22:36.820here's copying over my source code,
00:22:37.140 -->
00:22:38.280building a Docker image,
00:22:38.480 -->
00:22:40.300and then turning that base Docker image
00:22:40.400 -->
00:22:42.620into an executable that I can distribute, right?
00:22:42.860 -->
00:22:44.160But this thing would do that.
00:22:44.300 -->
00:22:46.040It's just the example doesn't show that.
00:22:46.340 -->
00:22:47.980So I think that is pretty neat.
00:22:48.280 -->
00:22:57.180And 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.200That's pretty cool.
00:22:58.580 -->
00:23:00.420Well, do they have to have Docker?
00:23:00.840 -->
00:23:01.040Yes.
00:23:01.360 -->
00:23:01.560Okay.
00:23:01.920 -->
00:23:11.240Because 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.420The 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.240Also, again, the reason I don't like this example here,
00:23:35.470 -->
00:23:36.940boy, they just like bundle up Linux.
00:23:37.500 -->
00:23:41.060In the Docker file itself, you can say expose this port
00:23:41.300 -->
00:23:44.240and do other type of things like that in the Docker file.
00:23:44.670 -->
00:23:46.740So you can bake in, like if you're saying,
00:23:47.160 -->
00:23:50.880my thing is a little Flask app that runs using maybe SQLite for the database.
00:23:51.900 -->
00:23:54.540It exposes this port so that you can then, you know,
00:23:54.580 -->
00:23:57.460it could print out a thing like click here to like talk to the server
00:23:57.940 -->
00:24:00.360and launches a URL, the web browser to like a local URL.
00:24:00.760 -->
00:24:04.860So 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.740But yeah, anyway, you want to ship a Docker image as just an executable binary?
00:24:10.030 -->
00:24:10.600Check this thing out.
00:24:10.880 -->
00:24:11.140Cool.
00:24:11.830 -->
00:24:12.080Very neat.
00:24:12.420 -->
00:24:12.800All right.
00:24:12.980 -->
00:24:14.760I know that you have extras and I don't.
00:24:15.120 -->
00:24:15.300Okay.
00:24:15.680 -->
00:24:18.020I've got some testing code news.
00:24:18.200 -->
00:24:19.800So testing code has sort of been on pause.
00:24:19.970 -->
00:24:20.800When was the last time?
00:24:20.880 -->
00:24:21.580May 7th.
00:24:22.960 -->
00:24:23.940It's July now.
00:24:24.440 -->
00:24:26.800It's been on pause for a while because of life.
00:24:26.980 -->
00:24:29.060But life has got a little bit of room in it now.
00:24:29.160 -->
00:24:31.120So I'm going to do a little bit of testing code.
00:24:31.370 -->
00:24:32.200So there is a,
00:24:32.620 -->
00:24:36.800there's three episodes in the queue and hopefully more to come.
00:24:37.080 -->
00:24:37.520So at least,
00:24:38.120 -->
00:24:42.120and so this week there will be an episode and I'm pretty excited because the,
00:24:42.250 -->
00:24:46.100this week's episode is covering pytest Django with Adam Johnson.
00:24:46.280 -->
00:24:48.220And a lot of people have asked me about pytest Django.
00:24:48.700 -->
00:24:52.000And since I am not a Django expert,
00:24:52.440 -->
00:24:52.580I,
00:24:53.220 -->
00:24:54.100and Adam is,
00:24:54.520 -->
00:24:56.160Adam was willing to talk with me about it.
00:24:56.320 -->
00:24:56.960And it's a,
00:24:57.340 -->
00:24:58.540it's a really great discussion.
00:24:58.900 -->
00:25:00.680So I'll be excited to get that out.
00:25:01.500 -->
00:25:02.880I'll be excited to get that out this week.
00:25:03.130 -->
00:25:05.260And then also I went ahead.
00:25:05.550 -->
00:25:11.240So I'm using a hosting provider I'm using for this is Transistor FM.
00:25:11.490 -->
00:25:16.420And Transistor has the ability to push things to YouTube, but it's a little bit wacky.
00:25:16.620 -->
00:25:18.760So I have went ahead and turned that on.
00:25:19.260 -->
00:25:22.480And so everything now is for testing code.
00:25:22.590 -->
00:25:23.580Well, not everything yet.
00:25:24.340 -->
00:25:27.140207 episodes are now up on YouTube.
00:25:27.840 -->
00:25:31.660The catch is they sort of show up in random order.
00:25:32.780 -->
00:25:37.300They push, I think, 80 episodes a day or something like that.
00:25:37.960 -->
00:25:38.840They do a lot.
00:25:39.000 -->
00:25:42.820So by the time you probably hear this,
00:25:42.900 -->
00:25:44.540if you don't hear it on the first day,
00:25:45.360 -->
00:25:46.660they'll probably be all up.
00:25:46.880 -->
00:25:49.780But anyway, they're there, but they're just in weird order.
00:25:50.000 -->
00:25:52.140That's a lot of content to be pushing up there.
00:25:52.300 -->
00:25:55.200I suspect, though, after it sort of bulk uploads,
00:25:55.400 -->
00:25:58.500Once it does, as you start publishing, it'll go in the right order.
00:25:58.720 -->
00:26:01.240Yeah, well, it's because YouTube orders,
00:26:01.580 -->
00:26:06.340I'm pretty sure YouTube orders in the upload order of when you uploaded it.
00:26:06.560 -->
00:26:08.560And there's no way to backdate it to say,
00:26:08.880 -->
00:26:10.980this was actually two years ago or something.
00:26:11.100 -->
00:26:14.400Yeah, and probably Transistor just batch processes them.
00:26:14.500 -->
00:26:16.780And the order in which they complete is the order in which they upload.
00:26:17.320 -->
00:26:23.220Luckily, when I transitioned to Transistor, though,
00:26:23.560 -->
00:26:26.760They do have a, it just uses your old post dates.
00:26:27.200 -->
00:26:30.080So even though I transitioned like, I don't know,
00:26:30.160 -->
00:26:31.740a couple of years ago or something like that,
00:26:32.620 -->
00:26:34.540I still was able to, you know,
00:26:34.830 -->
00:26:37.700like some of the early episodes still show up
00:26:37.980 -->
00:26:40.080as like being happening in 2016,
00:26:40.670 -->
00:26:42.200even though they got transferred later.
00:26:42.500 -->
00:26:44.080But anyway, a lot of content.
00:26:44.390 -->
00:26:44.760Good deal.
00:26:45.100 -->
00:26:46.180So no extras for you?
00:26:46.580 -->
00:26:48.180No, I'm not very extra.
00:26:48.590 -->
00:26:49.740Just been working hard and stuff.
00:26:49.800 -->
00:26:51.020I'll share, I'll have things to share eventually,
00:26:51.280 -->
00:26:52.640but right now, no extras.
00:26:53.080 -->
00:26:54.620I do have a joke I've brought for us.
00:26:54.620 -->
00:26:55.080What do you think?
00:26:55.460 -->
00:26:55.920Oh, yeah.
00:26:56.160 -->
00:26:56.440Let's do a joke.
00:26:56.440 -->
00:26:56.860Are you here for it?
00:26:57.160 -->
00:26:57.260Yeah.
00:26:57.340 -->
00:26:57.540Okay.
00:26:57.920 -->
00:27:01.840So, Brian, what if programmers were doctors?
00:27:02.360 -->
00:27:03.140You know, the old joke.
00:27:03.980 -->
00:27:05.180Hey, doctor, my leg hurts.
00:27:05.900 -->
00:27:06.180Well, stop.
00:27:07.520 -->
00:27:09.500My leg hurts when I lift it like this.
00:27:09.500 -->
00:27:11.060Well, stop lifting it like that, right?
00:27:11.300 -->
00:27:11.440Yeah.
00:27:11.940 -->
00:27:15.420There's a fun variation on that joke for programmers,
00:27:16.060 -->
00:27:19.400and it's basically the doctor equivalent of it works on my machine.
00:27:20.080 -->
00:27:22.420It says, doctor, my leg hurts.
00:27:22.700 -->
00:27:23.180That's weird.
00:27:23.270 -->
00:27:24.880I also have a leg and it doesn't hurt.
00:27:24.950 -->
00:27:26.200The issue must be on your side.
00:27:29.760 -->
00:27:31.620Yeah, that's definitely true.
00:27:32.160 -->
00:27:32.240Yeah.
00:27:34.400 -->
00:27:34.900I like it.
00:27:35.220 -->
00:27:36.080That's dumb, but funny.
00:27:36.300 -->
00:27:36.700I know.
00:27:36.860 -->
00:27:37.800It's short and sweet.
00:27:39.140 -->
00:27:39.600I like it.
00:27:39.800 -->
00:27:39.960Cool.
00:27:40.360 -->
00:27:41.780Well, thanks for the joke, Michael.
00:27:42.010 -->
00:27:44.440And thanks, as always, for this wonderful episode.
00:27:45.160 -->
00:27:45.540You bet.
00:27:46.400 -->
00:27:46.740Bye, Brian.
00:27:46.920 -->
00:27:47.260Bye, everyone.
00:27:47.300 -->
00:27:47.400Bye.