WEBVTT

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

00:00:05.580 --> 00:00:12.120
This is episode 476, recorded April 6th, 2026.

00:00:12.500 --> 00:00:13.500
So many sixes, Brian.

00:00:13.800 --> 00:00:14.700
I'm Michael Kennedy.

00:00:15.000 --> 00:00:15.700
And I'm Brian Okken.

00:00:15.980 --> 00:00:17.780
And this episode is brought to you by us.

00:00:17.960 --> 00:00:23.680
We have all sorts of awesome things to help you get better at Python, testing and so on.

00:00:23.800 --> 00:00:24.680
Check the show notes.

00:00:25.060 --> 00:00:25.900
Subscribe to the newsletter.

00:00:26.100 --> 00:00:28.180
We've got a bunch of cool things we're sending out there.

00:00:28.180 --> 00:00:39.360
And every week, one of them, every week is a nice, extra rich summary of what we talked about, what we're covering, but also additional places to find more materials.

00:00:39.360 --> 00:00:42.420
So you can get a little bit of the background information.

00:00:42.660 --> 00:00:45.020
Like we might talk about, oh, here's how you do this thing in CI.

00:00:45.280 --> 00:00:50.080
But maybe the newsletter also includes details of getting started with CI and other things like that.

00:00:50.260 --> 00:00:51.580
And that's all thanks to Brian.

00:00:51.700 --> 00:00:52.660
So thank you for that.

00:00:52.900 --> 00:00:56.760
With that, you know, let's migrate it over to Brian's topic.

00:00:56.900 --> 00:00:57.260
What's up?

00:00:57.260 --> 00:01:00.080
I am talking about Ty migration.

00:01:00.480 --> 00:01:03.680
So Ty, T-Y, the Astral Project for type checking.

00:01:04.540 --> 00:01:07.500
Have you used it?

00:01:07.720 --> 00:01:09.120
I am using it actively.

00:01:09.380 --> 00:01:09.780
Yes.

00:01:09.980 --> 00:01:10.460
I love it.

00:01:10.680 --> 00:01:10.800
Okay.

00:01:10.940 --> 00:01:13.400
So it's the speed thing.

00:01:13.920 --> 00:01:16.000
So Ty's all Rust.

00:01:16.140 --> 00:01:18.520
So it's way faster than a lot of other stuff.

00:01:18.800 --> 00:01:21.300
Firefly is Rust also, but Ty's still faster.

00:01:21.460 --> 00:01:22.420
I think Firefly is Rust.

00:01:22.920 --> 00:01:23.640
It is, I believe.

00:01:23.760 --> 00:01:24.040
Yes.

00:01:24.040 --> 00:01:24.480
Okay.

00:01:24.840 --> 00:01:30.220
But it's not, Firefly is not noticeably laggy.

00:01:30.600 --> 00:01:32.200
But mypy is.

00:01:32.380 --> 00:01:35.700
And mypy is definitely something I use in CI.

00:01:35.700 --> 00:01:51.220
But anyway, well, I'm bringing this up because there was a post by Sebastian Ramirez, and I just went away from it, that all of his Python projects like FastAPI, Typer, SQL Model, Async, or all of that stuff.

00:01:51.220 --> 00:01:55.860
It's all just using, it's all using Ty now for the primary type checker.

00:01:56.120 --> 00:02:03.960
And so I'm thinking, and I had tried on for a couple of projects, and it was a little bit pickier than I expected.

00:02:04.280 --> 00:02:06.140
So I tried it again.

00:02:06.140 --> 00:02:14.640
So I went ahead and migrated pytestCheck to use Ty, and I'm kind of doing a similar model, and it's going well.

00:02:14.800 --> 00:02:25.960
So anyway, he migrated that stuff, and then Tim Hopper wrote a blog post called Migrating from mypy to Ty, Lessons from FastAPI.

00:02:25.960 --> 00:02:33.920
And it's a pretty fun walkthrough of some of the things because it's really pretty easy to migrate.

00:02:34.080 --> 00:02:41.520
So the idea around it, I'm going to skip down to the end because he's like, well, if you want to do this yourself, what should you do?

00:02:42.900 --> 00:02:44.680
Add Ty alongside of mypy.

00:02:44.820 --> 00:02:49.780
And that's what I'm doing with pytestCheck, doing both mypy and Ty.

00:02:49.780 --> 00:02:56.220
Actually, I've got them as two different talks, runs, sections to run them separately.

00:02:56.560 --> 00:03:00.000
But locally, I'm just running Ty because it's so fast.

00:03:00.120 --> 00:03:03.680
It's like it runs and it's done, whereas mypy, I have to wait a few seconds.

00:03:03.940 --> 00:03:07.460
A few seconds, not a big deal, but it makes it so that I don't run it as much.

00:03:08.220 --> 00:03:10.580
Anyway, add Ty alongside mypy.

00:03:11.080 --> 00:03:17.800
There's one setting that I turned on because of this article of errors on warning.

00:03:17.800 --> 00:03:24.900
So there's some Ty warnings that show up and they don't stop a Sierra run, but I want it to.

00:03:25.060 --> 00:03:27.320
I want to have it completely clean with no warnings.

00:03:27.800 --> 00:03:29.640
So you can set error on warning is true.

00:03:30.900 --> 00:03:43.440
If there's parts where you have to comment out, like you have to ignore certain lines for some reason, the annoying part right now is that you have, and I'll go back up to this discussion up here,

00:03:43.440 --> 00:03:46.040
is that you have to do two.

00:03:46.280 --> 00:03:51.980
So the ignore line for mypy is different than Ty in some of these.

00:03:52.320 --> 00:03:55.780
So that is a bit annoying if you're supporting both.

00:03:56.100 --> 00:04:01.460
But apparently it got better in Ty 0.0.25.

00:04:01.860 --> 00:04:02.580
Oh, come on, guys.

00:04:02.660 --> 00:04:03.580
You got to like, anyway.

00:04:04.320 --> 00:04:05.880
Those zero-vers are just killing me.

00:04:05.880 --> 00:04:08.560
That's a very, very, very zero-ver.

00:04:09.780 --> 00:04:11.480
0.0 is the version.

00:04:12.720 --> 00:04:16.800
But support's a better way to say ignore these sets of things.

00:04:16.920 --> 00:04:17.840
Apparently that works.

00:04:18.260 --> 00:04:22.080
So anyway, check out the model if you want to have ignores.

00:04:22.440 --> 00:04:26.160
I don't actually have, I think I have a few ignores, but not too many.

00:04:26.160 --> 00:04:31.740
And a lot of them are because I intentionally try to do something bad to simulate an error.

00:04:32.640 --> 00:04:32.760
Okay.

00:04:33.060 --> 00:04:38.500
So how it fits in the workflow also, it's really easy to run, all that stuff.

00:04:38.600 --> 00:04:39.040
It's great.

00:04:39.340 --> 00:04:39.540
Okay.

00:04:39.800 --> 00:04:40.380
Let's get him back.

00:04:40.460 --> 00:04:43.680
So you accept the double comments and then pick a smaller project.

00:04:44.200 --> 00:04:52.400
And whereas like on the FastAPI world, SQL model is now completely Ty only and doesn't use mypy apparently.

00:04:52.400 --> 00:04:56.880
So drop mypy when the noise exceeds the signal.

00:04:57.280 --> 00:04:58.060
So, right.

00:04:58.280 --> 00:05:05.620
So the SQL model had too many, like it wasn't worth having supporting both because of all the different ignores and all that stuff.

00:05:05.840 --> 00:05:11.580
I'm currently, I've had so many people request mypy support for pytest check.

00:05:11.700 --> 00:05:14.280
So I'm not planning on dropping it anytime soon.

00:05:14.420 --> 00:05:21.320
And for a lot of packages, I believe that's probably going to be the case of people just supporting both for a long time.

00:05:21.320 --> 00:05:26.520
And one of the really unfortunate things about how Python typing works, there's a lot of good things about it.

00:05:26.560 --> 00:05:27.920
So don't call me a hater.

00:05:28.220 --> 00:05:38.080
But one of the really unfortunate things is even amongst ty and Pyrefly, there are different conventions and warnings and things that count as errors and things that don't count as errors.

00:05:38.400 --> 00:05:39.960
Things that are checked and things that are not checked.

00:05:39.960 --> 00:05:47.180
And so if you have a project that needs to support two, both or more, you've got to put all of them in there.

00:05:47.400 --> 00:05:55.560
You've got to deal with cases where your type checker says everything's fine, but someone else type checker doesn't think it's fine.

00:05:55.640 --> 00:05:56.880
You're like, are you serious?

00:05:56.880 --> 00:06:03.140
Like the most ridiculous version of this is for one of my templating packages.

00:06:03.400 --> 00:06:09.520
I can't remember which one, like chameleon partials or flask chameleon.

00:06:09.580 --> 00:06:14.900
One of those where it like lets you do something with the, or maybe Jinja partials.

00:06:14.960 --> 00:06:15.380
I can't remember.

00:06:15.480 --> 00:06:17.760
But somebody was doing something with flask.

00:06:17.760 --> 00:06:26.080
And when you put the decorator onto a view that the web framework calls, it changed the signature in a very subtle way.

00:06:26.160 --> 00:06:29.760
And the signature is extremely complicated at the definition level.

00:06:30.080 --> 00:06:39.480
I mean, it's like multiple lines of what the warrant of the signature problem was, you know, not just like expected in, but it was really a lot of like stuff going on there.

00:06:39.500 --> 00:06:40.680
And it said that that was wrong.

00:06:40.740 --> 00:06:41.660
I'm like, who cares?

00:06:41.840 --> 00:06:42.720
No one's ever calling it.

00:06:42.760 --> 00:06:44.340
Like no one calls a view function.

00:06:44.340 --> 00:06:58.720
It didn't, nobody gave it a definition that it's this big complicated thing, but somehow the framework, you know, like it was just, I think the something about the app.get decorator expressed a type and then it changed that type.

00:06:58.940 --> 00:06:59.600
But who cares?

00:06:59.640 --> 00:07:00.740
Cause it's still ran just fine.

00:07:00.840 --> 00:07:07.280
So it was like, I had to go and spend hours reworking the signature of that thing to get it just right.

00:07:07.340 --> 00:07:11.060
So, so Pyrite would stop giving me a warning where I never even use Pyrite.

00:07:11.060 --> 00:07:13.540
I'm like, why am I spending my Saturday morning doing this?

00:07:13.620 --> 00:07:14.240
That's dumb.

00:07:14.580 --> 00:07:15.920
But people were using it.

00:07:15.980 --> 00:07:17.620
They would just get like loads of error.

00:07:17.780 --> 00:07:20.820
Like every viewpoint in their Flask function would have an error in Pyrite.

00:07:21.300 --> 00:07:26.220
Right now I have stuff in my code where it's got ignore statements for Pyrefly.

00:07:26.460 --> 00:07:31.880
But if I load it up in ty, ty gives me a warning that the ignore statement is unneeded.

00:07:31.960 --> 00:07:34.460
I'm like, it's not unneeded for you, but it's unneeded for that.

00:07:34.460 --> 00:07:39.380
Like, are you, anyway, this is why I say that.

00:07:39.380 --> 00:07:41.380
Like, it's just, it's a, it's a struggle.

00:07:41.380 --> 00:07:49.440
And if you create a library to your point, then you've got to put yourself in the situation where like, you don't have to work for a type checker that you choose.

00:07:49.440 --> 00:07:51.880
You work for everyone's type, all the type checkers.

00:07:51.960 --> 00:07:55.480
Cause whatever anyone may choose will become applied to your usage.

00:07:55.480 --> 00:07:55.840
Right.

00:07:55.900 --> 00:07:57.820
In their project, which is anyway.

00:07:58.160 --> 00:07:58.360
Rant over.

00:07:58.360 --> 00:07:58.560
Yeah.

00:07:58.880 --> 00:08:04.160
Luckily I haven't had too many issues with, after, after supporting my Pi, that was the big one.

00:08:04.220 --> 00:08:08.620
I think more people are using my Pi than anything else, but I think we'll see a shift.

00:08:08.920 --> 00:08:16.520
One of the things that I did want to call out also related to this is ty, ty, whatever you call it.

00:08:17.000 --> 00:08:18.280
Charlie says it's pronounced ty.

00:08:18.700 --> 00:08:19.120
TY.

00:08:19.320 --> 00:08:28.260
TY did catch one problem in pytest-check that I was like, oh, this, this, well, it's a hidden possible problem.

00:08:28.260 --> 00:08:41.120
And I just, this is quick, but I have support for pytest versions and, but I have runtime behavior that changes based on version because of, because of when pytest added features.

00:08:41.120 --> 00:08:52.940
so I was expecting, this version tuple to be integers and just comparing like, you know, is seven Oh three seven Oh, less than or equal seven Oh, sort of a thing.

00:08:52.940 --> 00:08:57.320
But the version tuple can be a string and ty caught that.

00:08:57.320 --> 00:09:03.340
And so I have, some code that I've converted to using, a thing called packaging.

00:09:03.620 --> 00:09:04.680
It's a packaging project.

00:09:04.820 --> 00:09:16.720
Packaging.version has a parse function and, and parse just creates an object that's comparable so that you can do, this whole version one equals less than or equal version two.

00:09:16.720 --> 00:09:20.860
So, and you, yes, you can probably write this function yourself, but why bother?

00:09:21.000 --> 00:09:21.780
It's right here.

00:09:21.860 --> 00:09:22.560
It's supported.

00:09:22.820 --> 00:09:23.600
use it.

00:09:23.860 --> 00:09:25.160
So anyway, very nice.

00:09:25.160 --> 00:09:25.720
Okay.

00:09:26.120 --> 00:09:26.740
Next topic.

00:09:27.140 --> 00:09:28.480
Next topic.

00:09:28.820 --> 00:09:32.340
I want to talk about a ORM built in rust.

00:09:32.540 --> 00:09:33.240
Very interesting.

00:09:33.660 --> 00:09:39.780
So this is basically an instead of Django ORM, which I think is really interesting.

00:09:40.000 --> 00:09:43.580
There's a lot of ORMs out there that are like, this is a great ORM.

00:09:43.640 --> 00:09:45.880
You can use it for anything except for you can't use it for Django.

00:09:46.040 --> 00:09:46.340
Sorry.

00:09:46.640 --> 00:09:47.060
Bye now.

00:09:47.380 --> 00:09:48.600
Why can't you use it for Django?

00:09:48.940 --> 00:09:54.580
Because Django so deeply cares about the models using the models to drive other things.

00:09:54.580 --> 00:09:55.860
The models generate forms.

00:09:55.860 --> 00:09:57.280
The models generate the admin.

00:09:57.280 --> 00:10:04.020
The, like the models and the way that ORM works in Django is kind of like what makes Django, Django.

00:10:04.240 --> 00:10:06.000
Like if you don't care about any of that stuff.

00:10:06.200 --> 00:10:06.940
Why are you using Django?

00:10:06.940 --> 00:10:09.140
Pick a micro framework, you know, whatever.

00:10:09.280 --> 00:10:09.500
Right.

00:10:09.640 --> 00:10:13.740
The reason you choose Django is because it has all these Lego blocks that you can use.

00:10:13.740 --> 00:10:13.960
Right.

00:10:14.160 --> 00:10:14.400
Okay.

00:10:14.400 --> 00:10:21.080
So this one called Oxide ORM, believe it is a, it's got to be written in Rust given that name.

00:10:21.240 --> 00:10:29.380
But what it is, it is a type safe, identic centric async ORM with a high performance Rust core.

00:10:29.720 --> 00:10:31.960
And it's inspired by Django.

00:10:32.140 --> 00:10:39.140
It's not necessarily, it doesn't necessarily sell itself explicitly replacing Django, but I will tell you why I made this call back in a second.

00:10:39.400 --> 00:10:40.520
So, Hey, this is a young project.

00:10:40.620 --> 00:10:41.240
Just heads up.

00:10:41.360 --> 00:10:42.360
So, you know, heads up.

00:10:42.400 --> 00:10:43.300
This is a young project.

00:10:43.300 --> 00:10:52.160
I bet it has a higher version than ty, but nonetheless, it's got basically the same syntax as Django, model.objects.filter, et cetera.

00:10:52.320 --> 00:10:55.420
It has full Pydantic V2 validation.

00:10:55.620 --> 00:10:59.420
So in this world, you create your models using Pydantic, right?

00:10:59.480 --> 00:11:01.320
Which has all the Pydantic goodness.

00:11:01.500 --> 00:11:03.700
We're going to come back to talking about this kind of stuff as well.

00:11:04.400 --> 00:11:04.560
Okay.

00:11:04.860 --> 00:11:10.540
But very cool to be using Pydantic for the data access classes.

00:11:10.540 --> 00:11:19.500
It's also async first built on modern Python, asyncio, Rust performance, SQL generation and execution is native in Rust.

00:11:19.500 --> 00:11:24.820
So like one of the things you got to do is, okay, you gave me a model called customers, which is a Pydantic class.

00:11:24.820 --> 00:11:31.160
What does that mean in terms of a SQL query when you say dot city equal equal, whatever, right?

00:11:31.240 --> 00:11:32.060
That kind of stuff.

00:11:32.280 --> 00:11:38.000
Multi-database support, of course, transactions, migrations, Django style migrations and a migrate CLI.

00:11:38.000 --> 00:11:42.800
So you can see it's like trying to be the ORM for Django without explicitly saying not quite.

00:11:42.920 --> 00:11:45.600
If you look at performance, there's a couple of things.

00:11:46.160 --> 00:11:47.600
Higher is better here.

00:11:47.840 --> 00:11:48.900
Let's just take the Postgres.

00:11:49.040 --> 00:11:56.600
With Oxide, it's 1,475 requests per ops per second versus Tortoise, which is 888.

00:11:56.940 --> 00:11:59.340
Piccolo, 930.

00:11:59.660 --> 00:12:01.120
Django, 730.

00:12:01.120 --> 00:12:08.100
So just almost exactly 2x of Django and 4x of SQLAlchemy, 4x of SQL Model.

00:12:08.360 --> 00:12:09.660
I mentioned, you know, tie back there.

00:12:09.920 --> 00:12:13.060
And holy moly, PeeWee is at 80 versus 1,400.

00:12:13.440 --> 00:12:14.660
So that's pretty wild.

00:12:14.940 --> 00:12:23.540
And then obviously all the type checking, you know, they've got this query of like, hey, here's how you would do a really long, complicated query in both in Django and Oxide.

00:12:23.600 --> 00:12:34.800
The only difference, literally the only difference in these, you know, probably a 90 line, 90 character 90 column rather, same thing, 90 column query on the ORM.

00:12:34.880 --> 00:12:38.220
The only difference is the await keyword because it's async, which is pretty sweet.

00:12:38.540 --> 00:12:39.580
So yeah, good stuff.

00:12:39.800 --> 00:12:42.640
Now back to Django a little bit more.

00:12:42.640 --> 00:12:45.240
It has Oxide Admin.

00:12:45.740 --> 00:12:51.360
So Oxide Admin is auto-generated admin panel for Oxide ORM with zero boilerplate.

00:12:51.660 --> 00:12:54.300
So it's got all the Django type of things here.

00:12:54.500 --> 00:12:59.880
And what's interesting is it's for FastAPI, Litestar, SanicCore, and Falcon adapters.

00:12:59.880 --> 00:13:01.360
Does it not have Django support?

00:13:01.440 --> 00:13:02.420
It's just Django-like.

00:13:02.680 --> 00:13:06.840
I think it might not actually even integrate with Django, but it gives you the Django style, right?

00:13:07.020 --> 00:13:10.100
It gives you the admin, gives you the migrations.

00:13:10.400 --> 00:13:13.340
It gives you the query syntax that's identical.

00:13:14.280 --> 00:13:16.440
I bet you could use it with Django somehow.

00:13:16.600 --> 00:13:17.680
Anyway, here it is.

00:13:17.960 --> 00:13:18.540
That's pretty cool.

00:13:18.880 --> 00:13:18.980
Yeah.

00:13:19.100 --> 00:13:19.520
Yeah, yeah.

00:13:19.520 --> 00:13:19.880
Pretty cool.

00:13:19.960 --> 00:13:28.340
It's always on the lookout for these interesting data access libraries that are creative, bring something new without, you know, being too weird, I guess.

00:13:28.340 --> 00:13:28.740
Yeah.

00:13:29.100 --> 00:13:33.080
So anyway, Django oxide, or oxide ORM, not Django oxide.

00:13:33.300 --> 00:13:33.940
Oxide ORM.

00:13:33.980 --> 00:13:34.340
Check it out.

00:13:34.700 --> 00:13:35.240
All righty.

00:13:36.400 --> 00:13:38.580
I actually, I've been thinking about types.

00:13:38.720 --> 00:13:39.960
So I'm going to go back to types.

00:13:40.480 --> 00:13:43.640
We have this theme, but we're just going to talk about the same thing for whatever our theme is.

00:13:44.600 --> 00:13:45.440
Yours is types.

00:13:45.560 --> 00:13:45.660
Go.

00:13:45.920 --> 00:13:46.540
Types today.

00:13:47.080 --> 00:13:50.620
So this suggestion comes to us from Emma Typing.

00:13:50.620 --> 00:13:53.580
So I think Emma cares about typing also.

00:13:53.980 --> 00:13:54.640
Very cool.

00:13:54.860 --> 00:14:03.080
She says a very cool project, which realizes something I've wanted for a long time, documentation for Python with type shed types.

00:14:03.740 --> 00:14:05.480
So what are we talking about?

00:14:05.560 --> 00:14:07.400
I did a comparison of side by side.

00:14:07.400 --> 00:14:18.480
So if we take a look at like the function, the docs.python.org at the built-in functions, you get like ABS, like absolute for absolute.

00:14:18.940 --> 00:14:20.240
And it takes a number.

00:14:20.520 --> 00:14:21.600
But what type is number?

00:14:21.920 --> 00:14:27.280
Well, this new project has, has the same, it's basically the Python docs, but with types.

00:14:27.520 --> 00:14:32.700
So how, how that function signature would look with types.

00:14:32.700 --> 00:14:34.260
And this is all from type shed.

00:14:34.460 --> 00:14:48.440
So, the project, that we're shouting out to is, well, it's a, it's a GitHub.io thing, but, it's an open source called type shedding CPython docs, a tool for, to find

00:14:48.440 --> 00:14:52.840
missing type shed stubs and generate type annotated CPython docs.

00:14:52.940 --> 00:14:56.340
So it takes the CPython docs and annotates them with types.

00:14:56.480 --> 00:14:57.120
It's pretty cool.

00:14:57.120 --> 00:15:07.080
if you, if you're looking at some of the, some of the internal stuff, and it, it's all doing, it looks like it's using Pyrefly, to generate some of these things.

00:15:07.200 --> 00:15:08.540
Maybe, I don't know, but.

00:15:08.860 --> 00:15:09.160
Interesting.

00:15:09.520 --> 00:15:09.720
Okay.

00:15:09.820 --> 00:15:11.100
That's pretty, that's pretty much it.

00:15:11.100 --> 00:15:15.920
just as, if you want to look at anything in the docs, but with types, there you go.

00:15:16.100 --> 00:15:18.620
like what is a slice take?

00:15:18.740 --> 00:15:22.420
Oh, slice takes, you know, well, it's going to be like.

00:15:22.560 --> 00:15:24.200
It's an any, any, not too much.

00:15:24.360 --> 00:15:24.560
Any, any.

00:15:24.960 --> 00:15:26.220
that.

00:15:26.660 --> 00:15:29.520
But there's somewhere probably, interval of or something.

00:15:29.840 --> 00:15:30.040
Who knows?

00:15:30.460 --> 00:15:30.620
Yeah.

00:15:30.900 --> 00:15:31.080
So.

00:15:31.080 --> 00:15:31.560
Pretty cool.

00:15:31.640 --> 00:15:32.300
I like this idea.

00:15:32.440 --> 00:15:32.920
That's great.

00:15:33.160 --> 00:15:40.920
I think these, these kinds of things are powerful too, because especially if you're doing any sort of AI thing, you would say, you know, you ask, is it do something that's like, what are you doing?

00:15:40.960 --> 00:15:41.960
You have no idea thing.

00:15:42.260 --> 00:15:47.900
Read the docs or check this out or I'm going to, you know, and if it has type information that really gives it a lot more to work with.

00:15:48.140 --> 00:15:48.460
Yeah.

00:15:48.500 --> 00:15:57.180
And also because like, that, that the docs, the Python docs are really for us, but type shed is really not for us.

00:15:57.180 --> 00:16:05.800
So the kind of the idea to combine the information in type shed and present it as the docs, it's pretty kind of, it's kind of a clever thing.

00:16:06.240 --> 00:16:06.260
So.

00:16:06.380 --> 00:16:06.540
Yeah.

00:16:06.580 --> 00:16:07.180
It's very clever.

00:16:07.520 --> 00:16:14.760
And also if you're doing something kind of like something built in, you can look at, you know, look at what the types are with that and try to match it.

00:16:14.900 --> 00:16:15.080
Yeah.

00:16:15.080 --> 00:16:15.580
Definitely.

00:16:15.580 --> 00:16:16.160
All right.

00:16:16.280 --> 00:16:20.320
Shall we keep with our theme or yeah, of, of theme swapping.

00:16:20.640 --> 00:16:22.740
So yeah, like to just keeping the theme going.

00:16:22.800 --> 00:16:24.800
So I want to talk about databases some more.

00:16:24.980 --> 00:16:25.260
Okay.

00:16:25.520 --> 00:16:37.580
And, and ORMs, but from a different perspective this time a little bit in the sense that I wrote an article, the article I want to talk about is raw plus DC database pattern, a retrospective.

00:16:37.640 --> 00:16:44.240
This is, this is pretty interesting, but in order to talk about, let me just talk about the design pattern that I published real quick.

00:16:44.560 --> 00:16:48.540
So people maybe didn't read that article or they didn't listen a couple episodes ago or something.

00:16:48.880 --> 00:16:49.040
Right.

00:16:49.280 --> 00:17:00.140
So my thesis is there's a couple of reasons why depending on maybe smaller ORM libraries, it's not that great of an idea necessarily.

00:17:00.540 --> 00:17:11.620
One is if that thing goes unsupported, you've got a good chunk of your application and a foundational portion of it that is tied to this potentially unsupported thing.

00:17:11.880 --> 00:17:13.860
So make that concrete Talk Python Training.

00:17:14.000 --> 00:17:20.020
When I created it, it was based on Mongo engine and that was a up and coming super nice query syntax.

00:17:20.180 --> 00:17:25.740
Also models exactly the Django ORM by the way, by oxide, like oxide ORM.

00:17:26.000 --> 00:17:32.740
So a lot of the queries in there looked very much like they were Django, but they were object-based going to MongoDB.

00:17:32.940 --> 00:17:33.020
Right.

00:17:33.020 --> 00:17:34.260
So that's great.

00:17:34.340 --> 00:17:41.220
But fast forward 10 years and that thing's had a couple of releases that like changed the read me in five years.

00:17:41.340 --> 00:17:49.380
Like, and for reasons, maybe I'll get to later, like another episode is I wanted to switch this to async.

00:17:49.600 --> 00:18:00.080
I wanted to switch Talk Python Training to async, but this older ORM library was not ever, ever going to move to adopt an async model.

00:18:00.080 --> 00:18:00.560
Right.

00:18:00.560 --> 00:18:04.200
Even if it doesn't throw away its synchronous model, like have an async option.

00:18:04.380 --> 00:18:08.260
Like, no, I mean, if it's all that's happening is like a couple of read me's are getting updated.

00:18:08.420 --> 00:18:10.960
There's no, there's no way they're rewriting the whole thing to be async.

00:18:11.040 --> 00:18:11.580
You know what I mean?

00:18:12.260 --> 00:18:12.660
Yeah.

00:18:12.660 --> 00:18:15.300
And it's a, it's also probably a scratch your own itch sort of thing.

00:18:15.300 --> 00:18:19.220
If they, if the current project does what they needed to do, why, why update it?

00:18:19.380 --> 00:18:19.580
Yeah.

00:18:19.600 --> 00:18:23.020
And they're not obligated to, but I'm also not obligated to keep using it.

00:18:23.020 --> 00:18:23.220
Right.

00:18:23.760 --> 00:18:24.200
Yeah.

00:18:24.200 --> 00:18:35.940
So that was, that's number one is like, is there a more stable base and the stable basis, whatever the core database driver package is.

00:18:35.940 --> 00:18:43.380
In this case, it's PyMongo, but you know, could be just the core one from the Postgres team talking to Postgres with Python.

00:18:43.520 --> 00:18:43.820
Right.

00:18:43.820 --> 00:18:46.780
Like that's what all these ORMs are based on often anyway.

00:18:46.980 --> 00:18:52.220
I mean, oxide ORM is not quite, probably based on the Rust version that the Postgres team provides or whatever.

00:18:52.340 --> 00:18:52.500
Right.

00:18:52.700 --> 00:18:53.520
So something like that.

00:18:53.580 --> 00:18:54.680
So that's one.

00:18:54.760 --> 00:19:07.600
The other one, the raw part has to do with this theory that I have that talking to, when you're working with agentic AI, they know frameworks, but what they really know super well is the foundational stuff.

00:19:07.600 --> 00:19:12.420
Like they probably know SQL model, but they really know SQL, you know?

00:19:12.640 --> 00:19:12.860
Yeah.

00:19:12.860 --> 00:19:19.640
They probably know Mongo engine a little bit, but they really know just queer, like raw MongoDB queries.

00:19:19.840 --> 00:19:29.960
And so that's the idea is like, like, what if we just had a data access layer that was just raw queries and it returned some kind of rich classes like data classes.

00:19:30.180 --> 00:19:36.640
Now, before people email me, cause I've talked about this on Reddit and like three fourths of the comments were, why aren't you using Pydantic?

00:19:36.760 --> 00:19:37.920
You could use Pydantic here.

00:19:38.040 --> 00:19:40.520
Do you know that you're using data classes when you could use Pydantic?

00:19:40.740 --> 00:19:41.580
You have no type.

00:19:41.580 --> 00:19:42.800
Like, yes, sure.

00:19:42.860 --> 00:19:47.760
You want to call it raw plus Pydantic and use Pydantic instead is literally the same thing.

00:19:47.800 --> 00:19:49.260
You're totally welcome to do that.

00:19:49.260 --> 00:19:49.460
Right.

00:19:49.720 --> 00:19:50.720
I just wanted something simple.

00:19:50.860 --> 00:20:00.900
I found like I, my dream of Pydantic, like in Beanie and other, these ORMs or SQL model is you've got this ORM and it returns Pydantic models.

00:20:00.900 --> 00:20:06.300
And maybe you've got FastAPI and you could just like take the thing straight from the database and send it out over the API.

00:20:06.420 --> 00:20:07.600
And how amazing would that be?

00:20:07.880 --> 00:20:15.880
I'd not almost never was able to take the database model and return it directly as an API response.

00:20:16.040 --> 00:20:18.700
And so you're always like transforming them anyway.

00:20:19.080 --> 00:20:23.260
And so I ended up showing like, I just don't need, I don't need a Pydantic all the way down.

00:20:23.320 --> 00:20:25.380
Like Pydantic at the boundaries was good for me.

00:20:25.380 --> 00:20:25.780
Okay.

00:20:25.860 --> 00:20:29.380
So all that's so I can tell you about my retrospective.

00:20:29.380 --> 00:20:33.820
So I wrote up this thing where I rewrote Talk Python training.

00:20:34.040 --> 00:20:37.600
Now Python Bytes is already using this, so it already gets this benefit.

00:20:37.720 --> 00:20:40.960
But I did this for Talk Python training, the courses.

00:20:41.420 --> 00:20:45.580
I said, well, let me just put it into practice and see how it goes and if it's any better.

00:20:45.960 --> 00:20:48.280
So I came with pictures, Brian, look at these pictures.

00:20:48.820 --> 00:20:51.020
Mongo Engine, the raw plus DC pattern.

00:20:51.020 --> 00:20:56.400
It's almost 2x more requests per second for the same hardware, the same everything else.

00:20:56.660 --> 00:20:57.920
Just switching this out.

00:20:58.140 --> 00:21:02.440
And the bonus is you get a much more stable library to program against.

00:21:02.900 --> 00:21:04.240
And AI is really good.

00:21:04.300 --> 00:21:09.440
If you say, hey, AI, hey, Claude, create me an update function to do this thing.

00:21:09.600 --> 00:21:11.300
And it can do that better, right?

00:21:11.380 --> 00:21:15.620
It has a more, much, much, much, much more training information to do that with.

00:21:15.760 --> 00:21:17.340
And you get 2x performance.

00:21:17.340 --> 00:21:23.180
And even when you're doing 2x the number of requests, you still get quite a bit faster response time.

00:21:23.440 --> 00:21:23.940
That's pretty cool.

00:21:24.240 --> 00:21:24.360
Yeah.

00:21:24.480 --> 00:21:25.100
Pretty neat, right?

00:21:25.340 --> 00:21:25.520
Yeah.

00:21:25.660 --> 00:21:26.420
And you scroll down.

00:21:26.600 --> 00:21:31.440
Another bonus is actually about 10% less memory usage as well.

00:21:31.820 --> 00:21:32.480
That's good.

00:21:32.680 --> 00:21:38.660
Because apparently the ORM and the classes that it uses and the tracking and whatever just hangs on to junk more.

00:21:38.980 --> 00:21:39.380
I don't know.

00:21:39.520 --> 00:21:41.040
I mean, I'm still using classes, right?

00:21:41.160 --> 00:21:44.240
But for whatever reason, it's just quite a bit less memory usage.

00:21:44.240 --> 00:21:47.920
So I think that's turning out to be a pretty promising way to go.

00:21:48.720 --> 00:21:51.220
And yeah, expect more about this stuff later.

00:21:51.540 --> 00:21:53.160
So I can't resist.

00:21:53.700 --> 00:21:57.760
If you've already considered data classes, have you considered adders instead?

00:21:59.140 --> 00:21:59.860
Thank you.

00:22:00.300 --> 00:22:02.700
I want something built in.

00:22:03.060 --> 00:22:04.660
I want something with fewer dependencies.

00:22:05.220 --> 00:22:06.280
Data classes are built in.

00:22:06.400 --> 00:22:10.760
Pydantic and adders are both separate packages, right?

00:22:11.040 --> 00:22:11.260
Yeah.

00:22:11.260 --> 00:22:17.260
The benefit for choosing adders has to be big enough that it justifies taking on another dependency.

00:22:18.120 --> 00:22:20.080
So I talked about data class of wizards.

00:22:20.240 --> 00:22:26.820
So for this kind of stuff, I'm like, if I need to read and parse and validate data, I'm doing data class of wizards along with data classes.

00:22:26.960 --> 00:22:28.960
But people are certainly welcome to choose adders.

00:22:29.040 --> 00:22:30.340
They're welcome to choose Pydantic.

00:22:30.580 --> 00:22:32.000
They all work good in this model.

00:22:32.000 --> 00:22:43.080
Well, and especially to give other people their due for projects that already have Pydantic for other reasons, that makes sense also.

00:22:43.200 --> 00:22:43.620
Yeah, yeah.

00:22:43.660 --> 00:22:44.100
Absolutely.

00:22:44.480 --> 00:22:44.780
Absolutely.

00:22:45.240 --> 00:22:46.040
All right.

00:22:46.240 --> 00:22:46.620
All right.

00:22:47.180 --> 00:22:48.060
How extra are you feeling?

00:22:48.720 --> 00:22:50.100
I've got one extra.

00:22:50.860 --> 00:22:51.280
All right.

00:22:51.380 --> 00:22:51.720
Tell us.

00:22:51.720 --> 00:23:05.620
Just this weekend, I released a 0.5 release of the, I know I haven't talked about this much lately, the Lean TDD book that I was working over the fall and over, I guess, over Christmas.

00:23:06.540 --> 00:23:10.140
But it's, I went through, I'm preparing to read it.

00:23:10.180 --> 00:23:11.840
So I want to release it as an audio book.

00:23:11.840 --> 00:23:16.820
And so in preparing for that, I have almost completely rewritten it.

00:23:17.420 --> 00:23:21.120
It's restructured, and I think it's a lot nicer.

00:23:21.540 --> 00:23:24.420
Now, that said, I'm still working on it.

00:23:24.560 --> 00:23:26.740
So I think it's really solid now.

00:23:26.820 --> 00:23:30.140
I think it's a good resource to go ahead and run with.

00:23:31.060 --> 00:23:39.200
But I'm doing a third pass through right now with some comments from some trusted readers.

00:23:39.200 --> 00:23:45.880
And, yeah, anyway, there'll be some expansion, but not, I don't think, a huge direction shift in the future.

00:23:46.440 --> 00:23:48.520
Because I really want to get this done and wrapped up.

00:23:48.620 --> 00:23:51.600
But anyway, I'm pretty happy with this state right now, Lean TDD.

00:23:52.620 --> 00:23:56.620
And, yeah, if you're interested, let me know.

00:23:57.180 --> 00:23:57.840
So that's it.

00:23:58.120 --> 00:23:58.260
Awesome.

00:23:58.740 --> 00:23:59.140
Congrats.

00:23:59.460 --> 00:24:00.060
Getting there, huh?

00:24:00.400 --> 00:24:00.720
Yeah.

00:24:01.020 --> 00:24:01.900
I want to get this done.

00:24:02.100 --> 00:24:02.580
Yeah, indeed.

00:24:02.820 --> 00:24:03.200
All right.

00:24:03.280 --> 00:24:08.640
Let's see if I can not be jumped on by the un-auto-hiding of Vivaldi.

00:24:08.640 --> 00:24:12.720
So I want to talk about HiTest Just from Michael Booth.

00:24:12.780 --> 00:24:15.000
This is a really quick extra thing I want to give a shout-out to.

00:24:15.260 --> 00:24:15.440
Cool.

00:24:15.700 --> 00:24:17.400
And Just, you covered this a while ago.

00:24:17.480 --> 00:24:21.900
It's just a command runner, one of those sort of circular references type of thing.

00:24:22.200 --> 00:24:25.200
It's kind of like Make, kind of how I see it.

00:24:25.360 --> 00:24:26.740
You know, you've got some files.

00:24:26.860 --> 00:24:30.180
It tells you how to build things or test things or whatever, right?

00:24:30.280 --> 00:24:32.000
Like, was syntax inspired by Make?

00:24:32.040 --> 00:24:32.440
There you go.

00:24:32.660 --> 00:24:38.060
So Make is kind of long and gnarly and Just is more YAML-ish and cleaner, I think.

00:24:38.060 --> 00:24:39.320
I don't know.

00:24:39.880 --> 00:24:46.000
Anyway, it's a cool way to sort of structure automation for your projects, right?

00:24:46.660 --> 00:24:47.760
So a shout-out is...

00:24:47.760 --> 00:24:50.740
It's just lovely to work with if you haven't tried it yet.

00:24:51.000 --> 00:24:52.240
Yeah, I like it.

00:24:52.420 --> 00:24:57.640
So Michael Booth created a pytest-Just plugin.

00:24:57.640 --> 00:25:06.780
And it's a plugin that adds session-scoped Just fixtures to pytest so that you can test a Just file contracts directly in your test suite.

00:25:07.060 --> 00:25:14.000
So it lets you test, like, really nice things like just assert there exists a CI, a CI section.

00:25:14.580 --> 00:25:16.160
And that CI has a test.

00:25:16.680 --> 00:25:21.660
And it depends on test following the transitive dependencies of it and so on.

00:25:21.660 --> 00:25:23.260
So anyway, I thought that was kind of cool.

00:25:23.420 --> 00:25:28.640
So if you do stuff with Just and pytest, check out pytest-Just.

00:25:28.980 --> 00:25:29.240
Cool.

00:25:29.520 --> 00:25:33.460
All right, this next one needs a disclaimer before I go into it.

00:25:33.640 --> 00:25:34.720
I was not going to put...

00:25:34.720 --> 00:25:37.120
I was not going to talk about this, Brian, because I don't want to...

00:25:37.120 --> 00:25:39.400
You know, it's just not like TMZ bytes.

00:25:40.360 --> 00:25:41.120
Python bytes.

00:25:41.580 --> 00:25:42.540
There's a lot of drama here.

00:25:42.560 --> 00:25:46.300
But I think it's pervasive enough that I think we should put it on people's radar.

00:25:46.300 --> 00:25:48.040
So I'm not here...

00:25:48.040 --> 00:25:50.560
What I'm about to describe, I'm not here to take sides in.

00:25:50.720 --> 00:25:53.560
I'm not here to, like, bad light on anybody.

00:25:53.560 --> 00:25:59.480
But there's a mad level of turmoil around the encode projects.

00:26:00.240 --> 00:26:09.920
HTTPX, Django REST Framework, UVicorn, Starlette, which is the foundation of FastAPI, MKDoc.

00:26:10.000 --> 00:26:14.000
I mean, that is a mega set of dependencies that people depend upon.

00:26:14.000 --> 00:26:17.800
So I'm just going to link to four articles, five articles.

00:26:18.000 --> 00:26:20.860
And you all can read them and think about those however you want.

00:26:20.940 --> 00:26:24.220
But if you're using some of those libraries, you should probably have a look at this.

00:26:24.480 --> 00:26:29.320
So 320 upvotes on Reddit says, anyone know what's going on with HTTPX?

00:26:29.660 --> 00:26:33.680
Maintainer of HTTPX closed off access to issues and discussions last week.

00:26:33.940 --> 00:26:35.200
Hasn't had a release in a year.

00:26:35.400 --> 00:26:36.380
I wonder what's going on.

00:26:37.060 --> 00:26:42.440
Michelle says, I forked HTTPX to HTTPXXY.

00:26:42.440 --> 00:26:43.680
Talks about why.

00:26:43.940 --> 00:26:48.460
Transfer of UVicorn and Starlette causes a great bunch of stirs.

00:26:48.680 --> 00:26:51.540
There's an article called The Slow Collapse of MKDocs.

00:26:51.800 --> 00:26:52.040
Yeah.

00:26:52.220 --> 00:26:55.380
And that ties into Zensical, right?

00:26:55.700 --> 00:26:56.240
Yeah, exactly.

00:26:56.480 --> 00:27:08.800
And so basically this documents over time all the stuff that is going on that has led to Martin and his co-founder creating Zensical, which was material for MKDocs.

00:27:08.800 --> 00:27:13.060
But they found like, yeah, basically that this was becoming something they couldn't continue to work with.

00:27:13.100 --> 00:27:14.360
So they had to do something else.

00:27:14.620 --> 00:27:21.960
And Django REST framework is there's an inspiration to somebody's proposed that it gets taken into Django Commons.

00:27:22.500 --> 00:27:30.960
And Django Commons is this project to basically find and maintain packages that are important to Django that are kind of going unmaintained in a sense.

00:27:30.960 --> 00:27:35.380
And so in this conversation I'm linking to, there's a bunch of people like, this is kind of a good idea.

00:27:35.380 --> 00:27:40.360
But the people who are actually maintaining like, but I'm not really sure I'm going to come over and become the main maintainer.

00:27:40.500 --> 00:27:42.260
So it's sort of stalled out.

00:27:42.480 --> 00:27:50.920
Anyway, again, not trying to throw shade or call anyone out, but there's enough going on there with enough important projects that it's worth being at least awareness.

00:27:51.460 --> 00:27:51.580
Yeah.

00:27:51.840 --> 00:27:52.020
Yeah.

00:27:52.180 --> 00:27:52.520
Sad.

00:27:52.780 --> 00:27:53.060
Okay.

00:27:53.260 --> 00:27:53.900
Not sad.

00:27:53.960 --> 00:27:54.560
Something awesome.

00:27:54.680 --> 00:27:55.500
This is very awesome.

00:27:55.500 --> 00:28:01.720
This one I like quite a bit is announcing course completion certificates over at Talk Python.

00:28:01.920 --> 00:28:05.180
So I don't work in a big company, so that's not super relevant to me.

00:28:05.240 --> 00:28:10.100
So I have not really maybe given it as much love and attention as people want me to.

00:28:10.300 --> 00:28:11.500
So now I have.

00:28:11.740 --> 00:28:25.080
So what I've done is I have a nice dashboard that I released maybe a month ago and talked about that, I think, that shows you what courses you're working on, how far you are through them, which ones you recently have most recently taken at Talk Python and so on.

00:28:25.080 --> 00:28:31.600
But also, and if you have a bundle of like 50 courses, which a lot of people do, then it's like, well, here's a huge long list.

00:28:31.680 --> 00:28:32.700
Which one was I doing again?

00:28:32.820 --> 00:28:34.540
You know, it really helps like solve that problem.

00:28:34.640 --> 00:28:45.620
But now for the completed courses, it has a certificate section and you click that, it gives you a standard old PDF certificate that you can use if you have like continuing education

00:28:45.620 --> 00:28:51.280
requirements or something like that, where you need to submit an official certificate to your company or whatever.

00:28:51.380 --> 00:28:52.040
So that's kind of fun.

00:28:52.040 --> 00:28:59.620
But more importantly, more fun, I think, is there's an option to say, publish this as an official certificate on LinkedIn to my profile.

00:29:00.120 --> 00:29:10.340
So like one button click from Talk Python in your account, boom, takes you over, populates and fills an official certificate, even with a show your credential, which takes you back to an official.

00:29:10.600 --> 00:29:14.060
Yes, this credential is valid, verified for you page on LinkedIn.

00:29:14.620 --> 00:29:15.320
So that's pretty cool.

00:29:15.320 --> 00:29:15.680
Yeah.

00:29:15.840 --> 00:29:19.140
So if you take a bunch of courses, you know, and you want to have those there.

00:29:19.280 --> 00:29:22.440
So people go, oh yeah, they did actually take that flask class.

00:29:22.480 --> 00:29:22.960
That's right.

00:29:23.120 --> 00:29:28.440
They know a little bit more than, you know, maybe just flask is like a skill or something.

00:29:28.760 --> 00:29:28.740
So.

00:29:28.960 --> 00:29:31.500
Can I print it out and like pin it to my cubicle wall?

00:29:31.820 --> 00:29:32.360
You know what?

00:29:32.380 --> 00:29:34.980
You could print it out and get that on like a matte finish.

00:29:35.100 --> 00:29:35.320
Yes.

00:29:35.760 --> 00:29:36.040
Yeah.

00:29:36.100 --> 00:29:37.200
You can't even get the PDF.

00:29:37.300 --> 00:29:37.880
You could print it out.

00:29:37.880 --> 00:29:42.520
It's actually, it's actually like letter, US letter size.

00:29:42.880 --> 00:29:43.640
That's awesome.

00:29:43.920 --> 00:29:48.120
If it prints, like I had to get the DPIs of the image exactly right to fit for that.

00:29:48.220 --> 00:29:49.580
So anyway, that's fun.

00:29:49.680 --> 00:29:52.340
I thought not everyone's going to care about that, but people do.

00:29:52.440 --> 00:29:53.480
I think it's kind of neat to do.

00:29:53.720 --> 00:29:54.680
That's, that's pretty cool.

00:29:54.900 --> 00:29:55.120
Yeah.

00:29:55.240 --> 00:29:59.040
I've had a couple of those requests and I can't remember if I turned them on or not.

00:29:59.040 --> 00:30:02.280
I think Podia, the platform I use, I had them.

00:30:02.560 --> 00:30:02.980
So it just.

00:30:03.060 --> 00:30:03.300
Nice.

00:30:03.300 --> 00:30:09.640
I had a joke also, but I think let's save mine for next week and let's just use yours.

00:30:09.940 --> 00:30:10.220
Okay.

00:30:10.360 --> 00:30:11.260
Here's the funny things.

00:30:11.300 --> 00:30:14.120
I don't even remember what the joke is, but I'm going to look and then we're going to find out together.

00:30:14.400 --> 00:30:14.620
Oh yeah.

00:30:14.620 --> 00:30:15.000
Here we go.

00:30:15.040 --> 00:30:15.720
It's about AI.

00:30:15.960 --> 00:30:21.580
So this, I entitled this Noya Rich, the new rich in the eighties.

00:30:21.580 --> 00:30:22.600
Think Don Johnson.

00:30:22.820 --> 00:30:29.460
Think standing by a big mansion in Florida and Miami, Ferrari Testarossa in the back.

00:30:29.480 --> 00:30:31.340
Like this is rich in the eighties.

00:30:31.340 --> 00:30:37.000
Rich in 2026 in modal change the button color to green.

00:30:37.440 --> 00:30:39.460
Opus six, four, six.

00:30:39.680 --> 00:30:39.900
Yeah.

00:30:39.940 --> 00:30:41.600
It's which model are you using there?

00:30:42.120 --> 00:30:42.420
Yeah.

00:30:42.460 --> 00:30:50.760
And just like do like the completely most simple, a find and replaced or just a find and a type, the letter, the word green would take care of it.

00:30:50.800 --> 00:30:54.240
But no Opus four point six extra effort mode.

00:30:54.340 --> 00:30:55.980
Change the button color to green.

00:30:57.600 --> 00:30:58.300
That's funny.

00:30:58.600 --> 00:30:58.840
Yeah.

00:30:58.840 --> 00:31:03.200
It doesn't have a whole bunch of great comments, but it's pretty funny.

00:31:03.880 --> 00:31:04.700
So that's my joke.

00:31:05.060 --> 00:31:05.820
That is pretty funny.

00:31:05.980 --> 00:31:13.720
We, so April, we had like a changeover of our licensing stuff, March to April.

00:31:14.120 --> 00:31:20.760
And on March 31st, a lot of people were running, like got their tokens removed or something.

00:31:21.040 --> 00:31:22.080
And it's one day.

00:31:22.080 --> 00:31:26.740
But there was so many people complaining of like, I'm reported as out of tokens.

00:31:26.800 --> 00:31:27.460
How do I work?

00:31:28.120 --> 00:31:30.220
Hopefully you can still work without tokens.

00:31:30.520 --> 00:31:31.020
I know.

00:31:31.180 --> 00:31:32.080
It's so bad.

00:31:32.220 --> 00:31:33.180
It is so bad.

00:31:33.740 --> 00:31:34.820
It's so bad.

00:31:35.540 --> 00:31:39.420
It's like pre-smartphone, post-smartphone, you know?

00:31:39.420 --> 00:31:42.220
Well, I was just thinking that the other day.

00:31:42.480 --> 00:31:45.260
I was driving somewhere and I'm like, how?

00:31:45.540 --> 00:31:49.560
I'm in Portland and I've had GPS ever since I've lived in Portland.

00:31:49.980 --> 00:31:52.600
There's a lot of places I don't think I could find without a GPS.

00:31:53.200 --> 00:31:54.320
But you've been there before.

00:31:55.340 --> 00:31:56.200
Well, yeah.

00:31:56.200 --> 00:32:02.620
I've lived here for 15 years now and I, but still, I can't, I don't carry a plastic map or anything like that with me.

00:32:02.780 --> 00:32:02.860
So.

00:32:03.200 --> 00:32:03.360
Yeah.

00:32:03.360 --> 00:32:05.480
I don't know if I even have an Atlas in the car anymore.

00:32:05.640 --> 00:32:09.160
Like if for some reason my device broke and I was on a trip, I might just be there.

00:32:09.400 --> 00:32:11.580
I'm going to live here now.

00:32:11.700 --> 00:32:13.460
I guess we live here now.

00:32:15.480 --> 00:32:15.840
Yeah.

00:32:16.160 --> 00:32:17.020
This is funny.

00:32:17.360 --> 00:32:17.960
This is great.

00:32:18.320 --> 00:32:18.340
So.

00:32:18.420 --> 00:32:18.540
Yeah.

00:32:18.760 --> 00:32:19.760
Thank you, Brian, as always.

00:32:19.760 --> 00:32:22.580
Thanks everyone for listening and catch you all next week, everyone.
