Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to your earbuds.
This is episode 441, recorded July 21st, 2025, and I am Brian Okken.
Who am I? I am Michael Kennedy. Hello, everyone.
This episode is sponsored by DigitalOcean. Thank you, DigitalOcean.
Hear about them later in the show.
If you'd like to give us some topics for the show or just ask us questions or connect,
The links to Mastodon and Bluesky are in the show notes and on our webpage, pythonbytes.fm.
While you're there, you may as well go ahead and sign up for our newsletter,
or we'll send you links for the show afterwards.
And I apologize, because last week was a little late, because I've been sick, but I am bitter now.
And if you'd like to join the show, we do post it live, usually Tuesday mornings.
Monday, no, Monday mornings, sorry, Monday mornings at 10.
It used to be Tuesday.
But just go ahead and go over to pythonbytes.fm/live
and it'll tell you when the next one is.
And then even if we change it, you can see there.
Yeah, so let's kick it off.
What do you got for us, Michael?
Let's do some follow-up for our first item here.
So remember, last week I talked about RQ Lite,
the lightweight user-friendly distributed relational database built on SQLite.
And that was pretty cool.
You could build fault-tolerant relational databases in clusters,
even potentially geo-distributed using SQLite.
Well, as always, our wonderful listeners are like,
that's cool.
Did you know about?
Right?
Yeah.
I love that though.
Yeah.
I do too.
Yeah.
Thank you.
So two people wrote in and said,
did you know about? And one was Michael Booth. And Michael Booth said, you should check out
Terso, the next evolution of SQLite. So Terso is interesting in that it is kind of like SQLite,
but rewritten in Rust with the idea that it can be a distributed database type of thing. Also,
adding some of these AI features, as in vector search and other things that you need to be able
to support working with AI.
So pretty cool.
If you look through the website,
it's like their proper tercio.tech,
which it's cool, but I don't know.
It kind of leaves it a little bit unsure
like what exactly it is.
But like, what is this relation to SQLite, right?
So it's, go over to their GitHub,
it's a little bit clearer, right?
So it has SQLite compatibility.
It is not based on SQLite like RQ Lite is,
but it has language biodeans for Python
and other, maybe some of these other languages
people have heard of like Java and JavaScript and so on.
But it has asyncio support, which is really cool.
And you might say, well, it's SQLite.
Can it really have asyncio support?
Yes, at least on Linux.
It uses IO Uring to make that happen,
but it runs on all the server or all the platforms.
So you can like develop on macOS,
but then you get better perf on Linux, for example.
It also, they're working on adding a begin concurrent operation,
like a SQL statement or query statement,
and indexing for vector search,
like probably pretty much required,
and better alter support, right?
So basically think of it as a more modern take,
not just because it's rewritten in Rust,
which seems to be like the thing you do,
but as part of that,
taking on a lot of ideas like concurrency others. Okay. Big question. Is it ready for production
use? No, no. But these folks also worked on this thing called LibSQL. So it says that the
Tarot database is the next evolution of SQLite and Rust. They had previously worked on something
called LibSQL, which is an attempt to evolve SQLite in a similar direction, but through a fork
rather than a rewrite. And they're like, we think this new database thing is a better way to go.
So if what I talked about with RQ Lite inspires you, this also might.
They also have some kind of what you might expect as open core,
but we'll do the infrastructure side of things for you.
So they've got this thing called Terso Cloud,
which is a production or a paid sort of thing, right?
So serverless access or sync or whatever you want, right?
Replication, sync, vector search, all that kind of stuff.
Basically managed.
All right, so that's thing one.
the other one comes to it.
This is like all,
it's all Michael.
Michael's all the way down.
So we had Michael Booth recommend that one.
Got me,
Michael presenting it.
We got Mike Fiedler talking about the next one here.
And Mike Fiedler says,
have you heard,
well,
listen to the most recent Python bytes and RQ Lite.
It's cool.
Hadn't heard of that.
Something related that might be interesting is lightstream.io.
Okay.
Now lightstream.io is more SQLite than before.
You just keep using SQLite like the one built into Python.
but you launch a little daemon that watches it and constantly syncs it with S3 or Azure Blob
Storage or something like that. When you do that, what do you get? You get something pretty cool.
It just constantly streams changes to AWS S3, Azure Blob Storage, Google Cloud Storage, etc.
And that means you basically get failover. So imagine you're like, I would love to just have
a SQLite database that I can run on a simple little app.
But what if that server goes down?
What if that server fails?
Then all my data is lost from the last time I backed it up because my alternative was to
run some kind of cluster of Postgres or Mongo or whatever, right?
But then you're into lots of DevOps and managing those things, or you're paying for managed
databases, which can be relatively expensive to small servers like DigitalOcean has.
And what you can do is you can just keep on using regular SQLite,
but then use this Lightstream,
which has no real performance differences in terms,
like these other two things are like trying to change how SQLite works
or re-implement the compatible API.
This one just says, we promise to hook into some,
I put a link in the show notes.
There's a nice write-up about this that they did.
It's sort of like, why is this?
And called Lightstream revamped.
And it also just talks about like, why did they build this?
How does it work?
So it says it takes over the wall checkpointing process to continuously stream updates to
the database.
So basically as commits are committed to the SQLite file, it just keeps pushing that to
blob storage.
Cool, right?
So if that RQ Lite thing was interesting, but not quite what you're looking for, maybe one
of these two things is.
Yeah.
Interesting.
Pennies a day.
I like that.
Dirt cheap.
Yeah.
Dirt cheap.
only costs pennies a day because it's just blob storage, right?
So however you do that.
And the Lightstream, I don't know that they,
I don't see pricing.
How about that?
So this is just pure open source,
whereas the Terso was like,
you can do the open source thing yourself
or we can do our cloud thing, right?
I don't know which of those makes you happier,
but they're not the same in that regard.
Or they are the same with a wrapper, pay wrapper.
Anyway.
Yeah.
Okay, cool.
Awesome.
Over to you.
I want to talk about project status a little bit.
So I do want to talk about this new PEP.
So PEP 792 project status markers in the simple index.
So project status, what is this about?
So this is a new PEP that was, well, it's not right, really that.
It is pretty new.
So it actually updates in February of this year, and it's now accepted.
So I'm going to talk about this a little bit,
but I want to talk about project status and what this means.
So if I take a look at a couple of my silly projects,
I've got pytestCheck, which is a project I maintain,
and I actually intend people to be able to use it so it's maintained.
And so I have added a Trove classifier
to say the status of the project is production stable.
I think you can use it in production, but it's, you know, yeah.
But this is optional.
Maintainers don't have to put this in.
For instance, I also have another one called pytestCrayons
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
an it's an optional thing so what we have we have in the current situation there is the trove
classifiers that you can optionally add to um uh to to your project and you if you like for instance
start taking it down you can say that it's inactive it's not but i don't think very many people do
that often inactive just means somebody left but it's still they haven't updated a new one that's
like please close the door on your way out usually they just walk out the door so and then there's
also there's actually three kinds of status so there's that the trove classifiers but then there's
also uh indices uh can mark uh a district can mark distributions and releases as yanked so you can
like a version you can yank.
So it's not there anymore.
So then it's still like got an entry there,
but it's been a yanked, whether it's true or false.
You know what, I've started seeing that a lot lately, actually.
Have you?
At least uv pip warnings.
So I'll say, you know, update the dependencies of this project,
basically uv pip compile, and then I'll install the requirements.
And it'll say, this thing has been yanked because of,
and you don't have some reason, like broken metadata or whatever.
I'm like, well, I don't know what I'm supposed to do about this
because something needed it.
Yeah.
Okay.
I've not seen anything fail.
I haven't seen the apps no longer run because something got that status,
but I've seen warnings about this now.
And then there's another type of status that PyPI itself
can have a status for an entire project.
And the entire project can be quarantined
if the administrators of PyPI think that the project has been,
like, it's got, like, malware in it or something,
so they can quarantine it.
And also, a project owner can archive a project
and say, basically, you can disable new releases.
It's still around, but nobody can update to it,
and it's just archived.
It's there for historical purposes, I guess.
Anyway, so there's three statuses,
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.
So trying to clean that up a bit.
So there's really this proposals to have one project always has exactly one status and the status will be it'll be active.
And then there's some semantics around that active or archived or quarantined.
and deprecated and this makes sense so it's either either it's it's in use it's active or
it's not um but it's also what does it not mean it's either you know there's various levels of
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
have like to kind of consolidate these now this is this has been accepted this uh this active
archived, quarantined, deprecated status,
and it can only be one of those.
But this has been accepted.
However, it's not implemented yet.
So don't go out and look in PyPI
to try to find this yet.
So this just was accepted July 8th resolution.
So we will update you further
as we hear from people in the Python community
that this is all implemented.
Yeah, excellent.
I think I'm behind it.
The quarantine one is good.
I'm very glad I've never seen that warning.
And the archive one feels to me like a little bit of a supply chain safety type thing.
Like, I'm not going to mess with this.
I will never update this.
But let's not allow something to happen where someone else could either.
Well, and I'm not sure.
So I was trying to read this.
I was trying to understand kind of how it's going to be set.
So it's probably not going to be something just in the Trove classifier.
So it probably is going to be something that outside of the data itself.
So because there's times where it's clear there's nobody updating it anymore
and you can't get a hold of the maintainer,
you need to be able to say, this one's dead.
I think it's too bad that we don't have a better way to say,
but after the fact, if somebody just stops maintaining something,
hey, does anybody else want to start maintaining this?
I know there's security problems around that,
But, you know, it might be good to be able to have some things live on.
But yeah, and just archiving old stuff.
It's not good.
Was it GitHub?
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.
Or was it NPM?
I don't know.
One of these types of places was like, if stuff doesn't get touched, people are like, hey, it's not unmaintained.
It's just done.
There's nothing to add.
It's perfect.
works. Yeah. Exactly. Cool. All right. Well, before we move on, let's talk about our sponsor,
huh? Yeah. Yeah. So super excited to have DigitalOcean back to support the show. As always,
this episode is brought to you by DigitalOcean. And DigitalOcean is a comprehensive cloud
infrastructure that's simple to spin up even for the most complex workloads. And it's way better
value than most cloud providers. I've looked at the big three and they're not even close.
in how much value you get per dollar
and how much easier it is to use.
So at DigitalOcean, companies can save up to 30%
off their cloud bill.
They boast a 99.99% uptime SLA.
That means they promise to support that level of uptime.
Our experience, Brian, running Python Bytes
and other things on DigitalOcean for many years
was way higher than four nines.
Really, really reliable.
Love them.
They also have industry-leading pricing on bandwidth.
Also true, like 10 times, 8 times cheaper than AWS and Azure.
Really good.
So it's built to be the cloud backbone of businesses, small and large.
And now they have GPU-powered virtual machines, plus storage, database, networking capabilities, all-in-one platform.
So AI developers can confidently create apps that their users will love.
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.
DigitalOcean provides full-service cloud infrastructure that's simple to use,
reliable no matter the use case, and scalable for any business.
So did I say good value? $4 a month for a server.
That's yours. Your Linux server, USSH, too. Amazing.
And GPUs, servers for under $1 per hour.
So easy to spin up infrastructure to simplify even the most intense business demands.
That's DigitalOcean.
If you use the DO4bytes code, then you can get up to $200 in free credit to get started.
DO4bytes.
Just click the link in your podcast player show notes.
DigitalOcean is the cloud that's got you covered.
So like I said, please use our link.
You'll find the podcast player show notes, a clickable chapter URL while I'm speaking right now.
And at the top of the page at pythonbytes.fm for the episode.
Thank you to DigitalOcean for supporting Python Bytes.
Indeed, indeed.
Now, over to you.
Well, so I want to talk about testing a little bit.
So Hugo van Kaminade,
love you, Hugo, sorry,
wrote a post called Run Coverage on Tests.
And this is something that I've taught everybody to do
or taught everybody I can get hold of their ears to do this
because it's important.
But I'm glad that Hugo wrote a post about it.
And this was just going to be an extra.
However, I was blown away by a few things here.
And I'm like, oh, we got to make this highlighted a little bit more.
So the classic reason why you should run coverage on your test
is because of the copy-paste-modify problem with pytest.
Because pytest really kind of, the name of the test
is just sort of shows up in the reporting.
So it's very easy to copy my, you take an old test.
And since you're not calling the test function yourself,
it's easy to copy, paste, modify,
and forget to change the name.
And if you do that,
the second test just hides the name,
it hides the first one.
And that is the classic reason
why I tell people to run coverage
so that you don't do this.
And it's hard to figure out otherwise.
But a couple of cool things
that I learned from this post
is that Ruff's rule F811,
f811 catches that apparently so there is if you run ruff on your test code also with at least f811
turned on you'll it it checks for variables which the function name is officially a variable
that are defined and and redefined or otherwise shadowed and unused so if you used a variable
didn't use it and redefined it um that's just that's similar to defining a test twice so um i
didn't know that so i will make sure that i've got rough running on my test code also so that's cool
also a tip to say hey if you're really just copy paste modifying a test maybe think about py test
parameterization which i agree um you might want to just parameterize the test but this is also
pretty common it's so common to copy paste the second example is what the one that i really was
like oh my gosh we have to cover this because i would have never i wouldn't have caught this so
his second example is this weird okay so it's like a he's got an image some image stuff that he's
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
you've got some colors and images and you're trying to test in the in the end you're asserting
whether or not images are similar of a reloaded image versus an expected image and i don't really
understand what's going on here but um this isn't terrible but one of the things in here is that um
when he ran coverage it said that uh these two lines at the bottom are not being run at all and
he's got a a some images a set of images that he's iterating over and apparently there's nothing in
them so what's going on um and the thing that's going on apparently is uh this his his images are
a generator is what's going on so he's got some images that are being uh set up as a generator so
like a uh you know for i in a generator comprehension yeah generator comprehension
and then he passes that to a function that consumes the generator um and then uh and then when he tries
to use it as the as in the assert section there's nothing there anymore and i would have never like
obviously this is this is highlighted because i use generated way more than in generator
comprehensions more than i used to um because they're cool um and they're efficient and if
you've got huge things they use up less memory so i'm using them more and more and i probably am
using in my test i better make sure that i'm not like mucking up with things like this um so uh
pretty cool reason to use um uh use coverage on your test code to make sure that you don't do this
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.
Square brackets, not parentheses.
Yeah.
Problem solved.
It's subtle, though. I agree. Very subtle.
Oh, yeah. That's the difference, really? Yeah. A parentheses for those brackets, and then it works.
Well, yeah.
Okay. Anyway, thanks, Hugo.
Yeah. Thanks, Hugo.
All right, let's talk about something simpler than all that SQLite distributed async business.
Here's a problem, Brian.
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.
How do you do that?
Still, to this day, there's no amazing options to do that.
I know we have Py2.exe and Py2.app, but those things are, while awesome, they're not consistently
reliable and all sorts of things.
So this is a little bit more dev-focused than that.
But if you have Docker installed on the machine, right?
So if Docker is like Docker Desktop or OrbStack or whatever, and I guess even in the upcoming
macOS. We're supposed to have a built-in Docker equivalent built straight into macOS,
which should be interesting. Anyway, if you have any of those, there's this project called Docker
to EXE. Too executable. I believe it works on all the platforms. I don't think it's just
Windows EXE, even though, you know, yeah, it absolutely works on Windows, Linux, and Darwin,
aka macOS. So just too executable, right? And the idea is that if you have a Docker image
with an entry point or something like that that will start an app running,
you can simply build that as an executable, either a.exe on Windows, a.app.
You have to rename it, I think, more or less.
They're just executable binaries, right?
So you can take that Docker image and build it into an executable,
which is pretty awesome.
So you could, you know, the example they give is kind of, it's a bad,
I'm going to call it as a full-on bad example because it says,
here is a bare Linux image.
You can distribute that as a binary.
It's like, okay, great.
That's not exactly the use case.
You know, the use case is I have something
I want to give to someone and run it, right?
So as a Docker thing,
so maybe a better example would be like,
here's copying over my source code,
building a Docker image,
and then turning that base Docker image
into an executable that I can distribute, right?
But this thing would do that.
It's just the example doesn't show that.
So I think that is pretty neat.
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.
That's pretty cool.
Well, do they have to have Docker?
Yes.
Okay.
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.
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?
Also, again, the reason I don't like this example here,
boy, they just like bundle up Linux.
In the Docker file itself, you can say expose this port
and do other type of things like that in the Docker file.
So you can bake in, like if you're saying,
my thing is a little Flask app that runs using maybe SQLite for the database.
It exposes this port so that you can then, you know,
it could print out a thing like click here to like talk to the server
and launches a URL, the web browser to like a local URL.
So you can do that with, I'm pretty sure, with expose and other stuff in the Docker file.
But yeah, anyway, you want to ship a Docker image as just an executable binary?
Check this thing out.
Cool.
Very neat.
All right.
I know that you have extras and I don't.
Okay.
I've got some testing code news.
So testing code has sort of been on pause.
When was the last time?
May 7th.
It's July now.
It's been on pause for a while because of life.
But life has got a little bit of room in it now.
So I'm going to do a little bit of testing code.
So there is a,
there's three episodes in the queue and hopefully more to come.
So at least,
and so this week there will be an episode and I'm pretty excited because the,
this week's episode is covering pytest Django with Adam Johnson.
And a lot of people have asked me about pytest Django.
And since I am not a Django expert,
I,
and Adam is,
Adam was willing to talk with me about it.
And it's a,
it's a really great discussion.
So I'll be excited to get that out.
I'll be excited to get that out this week.
And then also I went ahead.
So I'm using a hosting provider I'm using for this is Transistor FM.
And Transistor has the ability to push things to YouTube, but it's a little bit wacky.
So I have went ahead and turned that on.
And so everything now is for testing code.
Well, not everything yet.
207 episodes are now up on YouTube.
The catch is they sort of show up in random order.
They push, I think, 80 episodes a day or something like that.
They do a lot.
So by the time you probably hear this,
if you don't hear it on the first day,
they'll probably be all up.
But anyway, they're there, but they're just in weird order.
That's a lot of content to be pushing up there.
I suspect, though, after it sort of bulk uploads,
Once it does, as you start publishing, it'll go in the right order.
Yeah, well, it's because YouTube orders,
I'm pretty sure YouTube orders in the upload order of when you uploaded it.
And there's no way to backdate it to say,
this was actually two years ago or something.
Yeah, and probably Transistor just batch processes them.
And the order in which they complete is the order in which they upload.
Luckily, when I transitioned to Transistor, though,
They do have a, it just uses your old post dates.
So even though I transitioned like, I don't know,
a couple of years ago or something like that,
I still was able to, you know,
like some of the early episodes still show up
as like being happening in 2016,
even though they got transferred later.
But anyway, a lot of content.
Good deal.
So no extras for you?
No, I'm not very extra.
Just been working hard and stuff.
I'll share, I'll have things to share eventually,
but right now, no extras.
I do have a joke I've brought for us.
What do you think?
Oh, yeah.
Let's do a joke.
Are you here for it?
Yeah.
Okay.
So, Brian, what if programmers were doctors?
You know, the old joke.
Hey, doctor, my leg hurts.
Well, stop.
My leg hurts when I lift it like this.
Well, stop lifting it like that, right?
Yeah.
There's a fun variation on that joke for programmers,
and it's basically the doctor equivalent of it works on my machine.
It says, doctor, my leg hurts.
That's weird.
I also have a leg and it doesn't hurt.
The issue must be on your side.
Yeah, that's definitely true.
Yeah.
I like it.
That's dumb, but funny.
I know.
It's short and sweet.
I like it.
Cool.
Well, thanks for the joke, Michael.
And thanks, as always, for this wonderful episode.
You bet.
Bye, Brian.
Bye, everyone.
Bye.