WEBVTT

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

00:00:04.740 --> 00:00:09.160
This is episode 270, recorded February 9th, 2022.

00:00:09.160 --> 00:00:10.580
I'm Michael Kennedy.

00:00:10.580 --> 00:00:11.680
And I'm Brian Okken.

00:00:11.680 --> 00:00:13.280
And I'm Dean Langsam.

00:00:13.280 --> 00:00:15.640
Dean, so great to have you on the show.

00:00:15.640 --> 00:00:16.300
Thank you.

00:00:16.300 --> 00:00:19.260
So often you help me with that start in the live chat.

00:00:19.260 --> 00:00:25.520
I know you're a big participant in the show, so we pulled you in and now here you are.

00:00:25.520 --> 00:00:25.940
Welcome.

00:00:25.940 --> 00:00:26.660
Thank you.

00:00:26.660 --> 00:00:27.520
Thank you.

00:00:27.660 --> 00:00:30.060
I've been a fan actually since episode one.

00:00:30.060 --> 00:00:31.180
I've been hearing this weekly.

00:00:31.180 --> 00:00:34.460
That goes back years, like five years or something.

00:00:34.460 --> 00:00:35.200
Yeah, it's about five years.

00:00:35.200 --> 00:00:39.860
I remember I moved apartments back then and I listened to Python.

00:00:39.860 --> 00:00:41.880
I didn't know Python as well back then.

00:00:41.880 --> 00:00:43.680
And I actually grow with the show.

00:00:43.680 --> 00:00:44.820
So that's very nice.

00:00:44.820 --> 00:00:45.480
That's fantastic.

00:00:45.480 --> 00:00:46.280
That's incredible.

00:00:46.280 --> 00:00:50.280
We've heard that from other people and that's just like mind-blowing to me.

00:00:50.280 --> 00:00:52.400
But yeah, it's cool.

00:00:52.400 --> 00:00:57.220
Yeah, I was taking like intro to data science classes in Coursera while listening to the show.

00:00:57.320 --> 00:01:00.660
And now other people call me a senior Python.

00:01:00.660 --> 00:01:02.520
So that was very nice.

00:01:02.520 --> 00:01:04.500
That's fantastic.

00:01:04.500 --> 00:01:05.760
And it does go fast.

00:01:05.760 --> 00:01:06.000
Yeah.

00:01:06.000 --> 00:01:07.120
So awesome.

00:01:07.120 --> 00:01:10.080
Thank you so much for joining us on the show.

00:01:10.080 --> 00:01:10.680
It's awesome.

00:01:10.680 --> 00:01:14.780
Before we get into it, I also want to say this episode is brought to you by Datadog.

00:01:14.780 --> 00:01:18.160
Check out their awesome stuff at pythonbytes.fm/datadog.

00:01:18.160 --> 00:01:20.320
I'll tell you more about that later.

00:01:20.880 --> 00:01:25.660
Right now, Python, I want to hear about a better Pygame loop.

00:01:25.660 --> 00:01:26.600
Brian, tell us about it.

00:01:26.600 --> 00:01:27.080
Yeah.

00:01:27.080 --> 00:01:29.940
So this is an article from Glyph.

00:01:29.940 --> 00:01:37.080
And Pygame is a package that's used for game programming a lot.

00:01:37.640 --> 00:01:39.480
And it's, I mean, a lot.

00:01:39.480 --> 00:01:45.780
And programming games is definitely, I think, it's one of the things I tried to do early on when I was a developer.

00:01:45.780 --> 00:01:55.340
And I think it's something that I think I encourage a lot of new developers to try out things like simple games because it's fun to learn coding that way.

00:01:56.040 --> 00:02:01.840
And it's, anyway, it's a big part of learning programming and the programming space.

00:02:01.840 --> 00:02:04.500
And with Python, it's pretty easy with Pygame.

00:02:04.500 --> 00:02:07.200
And there's a lot of tutorials out there.

00:02:07.200 --> 00:02:22.000
But one of the things that Glyph points out is a lot of the tutorials have this sort of simple while one loop where you, the main loop of a game where you just spin and wait for events and then handle the event or draw things or whatever.

00:02:22.000 --> 00:02:25.820
And then go back this and draw, you know, keep going.

00:02:26.020 --> 00:02:27.120
And this just happens forever.

00:02:27.120 --> 00:02:34.100
While one loop in programming is a busy loop and it's generally something that kind of has some issues.

00:02:34.100 --> 00:02:42.160
So Glyph is pointing out that some of the issues with this are that they, that it's, it's waste power for one.

00:02:42.160 --> 00:02:46.520
Your CPU is just spinning all the time when you're really not going to get events that fast.

00:02:46.520 --> 00:02:56.000
And then also there's a, there's a thing that I didn't know about called screen tearing, which is when you're drawing the screen at the same time you're trying.

00:02:56.000 --> 00:02:57.760
Writing to the screen buffer.

00:02:57.760 --> 00:02:58.780
Right.

00:02:58.780 --> 00:03:03.300
You're not waiting for the V sync 60, a hundred frames a second, whatever it is.

00:03:03.300 --> 00:03:03.480
Right.

00:03:03.480 --> 00:03:03.900
Yeah.

00:03:03.900 --> 00:03:08.960
So, and that can cause glitches in the game and it doesn't, it doesn't look as good.

00:03:09.460 --> 00:03:15.600
Py game does allow a V sync option, but apparently there's like some problem with that.

00:03:15.600 --> 00:03:25.740
So what really there's a, the article walks through both of these, both of these problems and the V sync fix, but, and the problems with that.

00:03:25.740 --> 00:03:34.920
But the end result really is he's got, it's actually interesting, interesting discussion about like really what's going on in py game.

00:03:34.920 --> 00:03:42.180
And he talks about like that, that there's really three jobs going on in the drawing and a game logic and the input handling all at once.

00:03:42.500 --> 00:03:44.440
And so this is a three thing.

00:03:44.440 --> 00:03:47.080
It's probably a good idea to do maybe async stuff.

00:03:47.080 --> 00:03:48.680
So things can work together.

00:03:49.520 --> 00:04:01.280
And, and the solution he came up with is still, I mean, it's definitely a larger loop, but it's not that big of a loop more complicated.

00:04:01.280 --> 00:04:10.260
And it's an async version to have some sleeps in there with some delays possibly, but a better loop for gaming.

00:04:10.260 --> 00:04:12.000
And it's, it's not that complicated.

00:04:12.000 --> 00:04:22.940
And actually if you're learning gaming while programming, hearing about this, these sorts of issues and, and trying to end learning how to solve it, it's probably just going to make you a better developer faster.

00:04:22.940 --> 00:04:25.800
So I think it's a good thing to, to look at this.

00:04:25.800 --> 00:04:26.640
Yeah.

00:04:26.640 --> 00:04:27.800
This looks really interesting.

00:04:27.800 --> 00:04:32.700
This game loop stuff, you know, it's, it's so often very much the same.

00:04:32.700 --> 00:04:41.640
And there's like these core elements like process input, you know, if the key's down or if there's a joystick attached, draw the scene, do the hit detection.

00:04:41.640 --> 00:04:43.600
And AI and game logic.

00:04:43.600 --> 00:04:45.580
And it's, it's almost always the same.

00:04:45.580 --> 00:04:49.740
Like this looks great as a way to tell me what I should be doing.

00:04:49.740 --> 00:04:58.420
And maybe the next step would be create a class that I just override, do the AI logic, draw the screen and just let that like not even be something I ever see.

00:04:58.420 --> 00:05:03.320
So this is ripe for a little bit of hiding away even this cool stuff.

00:05:03.320 --> 00:05:03.780
That's true.

00:05:03.780 --> 00:05:09.600
Maybe, maybe Pygame could extend a better built-in loop to hook into or something.

00:05:09.600 --> 00:05:10.520
Yeah.

00:05:11.140 --> 00:05:11.500
Yeah.

00:05:11.500 --> 00:05:23.540
I always think about, I always, I'm not actually used, do a lot of gaming on Python, but I always think about browsers, which are also kind of a loop that runs forever and renders stuff on your screen.

00:05:23.540 --> 00:05:26.920
And I think, well, the front end guys got it so easy, right?

00:05:26.920 --> 00:05:30.480
They don't, they just write the code and the browser does it for them.

00:05:30.480 --> 00:05:33.320
And I'm not sure if it works exactly the same.

00:05:34.100 --> 00:05:41.740
But maybe if someone manages to implement something that's like, just write your game and put it in this thing.

00:05:41.740 --> 00:05:47.820
Maybe this could attract more people into writing small games in Python.

00:05:48.320 --> 00:05:48.980
Yeah, absolutely.

00:05:48.980 --> 00:05:55.060
My thought is if you just sort of abstract that away, it's just 2D stuff, right?

00:05:55.060 --> 00:05:56.520
Which it's pretty easy to get into.

00:05:56.520 --> 00:06:08.060
I just listened to or watched a Netflix series called High Score, which is the history of video games going like way back to the Atari 2600 and Asteroid and whatnot.

00:06:08.320 --> 00:06:12.680
And there's this woman in here talks about how she got so inspired about just text-based games.

00:06:12.680 --> 00:06:16.940
So if you're learning to program, I definitely think games are a fun way.

00:06:16.940 --> 00:06:23.700
And often I think people might perceive that as like, well, I've got to write, you know, Angry Birds or something, which is fine.

00:06:23.700 --> 00:06:24.880
You can write that and that's super fun.

00:06:24.880 --> 00:06:32.060
But you can do a lot of stuff with just sort of text-based little fun story adventure type stuff as well.

00:06:32.060 --> 00:06:34.320
I got to check out that Netflix series.

00:06:34.320 --> 00:06:35.360
That sounds great.

00:06:35.360 --> 00:06:36.420
Yeah, yeah.

00:06:36.420 --> 00:06:44.520
I was just helping a friend writing like this small game and he's written this like with one thread and everything for this school project.

00:06:44.520 --> 00:06:50.160
And then he told me, well, but how do I show a score that like updates with the game?

00:06:50.160 --> 00:06:55.980
And then I thought like, no, for that you'll need multiple threads, a Pygame loop maybe and stuff like that.

00:06:55.980 --> 00:07:01.000
So if that could have been easier on him while learning Python, this could have been awesome.

00:07:01.000 --> 00:07:02.680
Yeah, absolutely.

00:07:03.080 --> 00:07:06.580
There's a lot of nice comments out in the live stream.

00:07:06.580 --> 00:07:11.340
Anthony says, I teach Pygame in my code club after school class.

00:07:11.340 --> 00:07:12.720
Smart kids, Pygame is great.

00:07:12.720 --> 00:07:17.020
So is Arcade, which is an alternative, an OpenGL-based alternative to Pygame.

00:07:17.020 --> 00:07:17.760
That's very cool.

00:07:18.340 --> 00:07:25.120
I do think having something visual for people when they're learning, it just, it reinforces things so much, right?

00:07:25.120 --> 00:07:32.120
Like writing that API back in that talks to database is great when you see the next three steps down the line, how it's going to enable something.

00:07:32.120 --> 00:07:35.640
But when you're getting started, you need quick feedback.

00:07:35.640 --> 00:07:36.260
Absolutely.

00:07:36.260 --> 00:07:37.300
All right.

00:07:37.880 --> 00:07:40.280
Well, let's talk about something else that's awesome here.

00:07:40.280 --> 00:07:42.200
I want to talk about SQLAlchemy.

00:07:42.200 --> 00:07:46.840
SQLAlchemy has been getting a lot of attention lately, and that's super cool.

00:07:46.840 --> 00:07:53.400
Mike Bayer released SQLAlchemy 2, which was the first async API version.

00:07:53.400 --> 00:07:57.120
So now you can use async and await with SQLAlchemy, which opens up lots of possibilities.

00:07:57.820 --> 00:08:05.300
Sebastian Ramirez released SQL Model, which is like a marriage of Pydantic and SQLAlchemy, which is also super neat.

00:08:05.300 --> 00:08:09.840
But there are many other things that you can do with SQLAlchemy that are really handy.

00:08:09.840 --> 00:08:16.460
So as all the awesome lists go, here's one for a curated list of SQLAlchemy.

00:08:16.460 --> 00:08:25.640
Now, first, just a word of warning from what I can tell, including the PR that I added yesterday, all the way back to the one in June 2020.

00:08:26.900 --> 00:08:30.400
It doesn't seem to be getting a whole lot of love, which is unfortunate.

00:08:30.400 --> 00:08:33.940
So it seems like it might be sort of stalled out.

00:08:33.940 --> 00:08:36.000
But that said, it's still a really good list of things.

00:08:36.000 --> 00:08:39.160
So I'll pull out a couple that I think are nice here.

00:08:39.160 --> 00:08:40.860
Which ones did I want to highlight?

00:08:40.860 --> 00:08:42.600
The first one is called Continuum.

00:08:42.600 --> 00:08:44.800
SQLAlchemy Continuum.

00:08:44.800 --> 00:08:46.220
And this is versioning.

00:08:46.220 --> 00:08:52.160
So imagine you would like to have a history or a record of changes to your database.

00:08:52.160 --> 00:08:54.320
Like maybe this is some sort of financial thing.

00:08:54.320 --> 00:09:02.300
And if you see changes, you want to be able to say, this person made this change on this date when they said, you know, update.

00:09:02.300 --> 00:09:04.260
Get the record.

00:09:04.260 --> 00:09:04.960
Make a change.

00:09:04.960 --> 00:09:07.360
And, you know, call commit on the SQLAlchemy session.

00:09:07.360 --> 00:09:12.160
So what this does is it will create versions of inserts, updates, and deletes.

00:09:12.160 --> 00:09:14.160
It won't store those.

00:09:14.300 --> 00:09:18.220
If there's not actually a change, it supports Olympic migrations.

00:09:18.220 --> 00:09:21.160
You can revert data objects and so on.

00:09:21.160 --> 00:09:23.760
So if you want that, SQLAlchemy Continuum.

00:09:23.760 --> 00:09:28.160
It's just like one of the many, many, many things in here, which is pretty awesome.

00:09:28.160 --> 00:09:30.320
Another one I wanted to highlight is UTC.

00:09:31.000 --> 00:09:38.880
So one of the challenges that people often run into is when you're storing stuff in the database, dates in particular, what time is that?

00:09:38.880 --> 00:09:45.320
Is that the time of the user who might be in a different time zone than the API endpoint that it was running at?

00:09:45.320 --> 00:09:45.860
Right.

00:09:45.900 --> 00:09:55.060
So it might be nice to be able to store time zone aware things and store them as UTC values so they're always the same.

00:09:55.060 --> 00:09:58.400
And then you can convert them back to the time zone, which is pretty cool.

00:09:58.400 --> 00:10:01.200
Another one is the SQLAlchemy Utils is pretty cool.

00:10:01.200 --> 00:10:06.080
So it's got things like choice type, which I'm guessing is basically enum.

00:10:06.080 --> 00:10:14.840
But country, JSON, URL, UUID, all of these different data types, data ranges, all kinds of stuff.

00:10:15.300 --> 00:10:18.960
ARM helpers, utility classes, and different things like that.

00:10:18.960 --> 00:10:21.580
So that's kind of a grab bag of them.

00:10:21.580 --> 00:10:22.320
Let's see.

00:10:22.320 --> 00:10:24.840
One also is called File Depot.

00:10:24.840 --> 00:10:27.180
There's cool stuff for processing images.

00:10:27.180 --> 00:10:33.740
You've got File Depot, which is a framework for easily storing and serving files out of your database on the web,

00:10:33.740 --> 00:10:38.600
as well as SQLAlchemy Image Attach, which is specifically about storing images in your database,

00:10:38.600 --> 00:10:41.460
which, by the way, we do, Brian, on Python Bytes.

00:10:41.460 --> 00:10:42.140
Cool.

00:10:42.660 --> 00:10:48.520
If you go to any page, any episode page, and you see that watch it on YouTube, that little thumbnail,

00:10:48.520 --> 00:10:52.640
we go get that dynamically from YouTube and then serve it up so we don't have to depend on YouTube.

00:10:52.640 --> 00:10:55.160
Anyway, that's pretty cool.

00:10:55.160 --> 00:10:56.120
Let's see.

00:10:56.120 --> 00:10:58.440
Maybe two more.

00:10:58.440 --> 00:11:00.400
There's Searchable.

00:11:01.040 --> 00:11:06.520
So if you want to add full-text search to your model, you can add, use this.

00:11:06.520 --> 00:11:11.120
And then it only supports Postgres because I'm sure it depends upon some core element there.

00:11:11.120 --> 00:11:15.220
But you can also do another one from MySQL as well, which is pretty cool.

00:11:15.220 --> 00:11:25.600
And then the last one is Schema Display, which generates basically graphs of your models and how they relate to each other, stuff like that, which is kind of neat.

00:11:25.600 --> 00:11:26.240
Nice.

00:11:26.240 --> 00:11:27.280
What do you all think?

00:11:27.280 --> 00:11:28.220
Cool stuff, right?

00:11:28.220 --> 00:11:28.600
Yeah.

00:11:28.600 --> 00:11:29.380
Very cool.

00:11:29.380 --> 00:11:30.720
Yeah.

00:11:30.720 --> 00:11:39.580
So if you're really bought into SQLAlchemy, you owe it to yourself to just flip through this list to just go like, wait, it can do that?

00:11:39.580 --> 00:11:41.520
I had no idea that it could do that, right?

00:11:41.520 --> 00:11:47.520
And just sort of see what are the other things that people built on top of here that I think would be super, super helpful.

00:11:47.520 --> 00:11:55.600
And by the way, my PR was really to say there's a layer called thin abstractions.

00:11:55.600 --> 00:12:04.440
And it says, you know, under the thin abstractions, we really should have us some SQL model because that thing is super popular straight out of the gate, right?

00:12:04.440 --> 00:12:05.980
So people should check this out.

00:12:05.980 --> 00:12:10.280
It's already got almost 7,000 stars and it's, what, a month old or something?

00:12:10.280 --> 00:12:10.960
That's crazy.

00:12:10.960 --> 00:12:11.600
Yeah.

00:12:11.600 --> 00:12:14.300
Maybe six weeks, but really, really new.

00:12:14.300 --> 00:12:15.980
Yeah.

00:12:15.980 --> 00:12:16.440
And.

00:12:16.440 --> 00:12:18.200
But the author, I mean.

00:12:18.200 --> 00:12:20.720
Yeah, exactly.

00:12:20.720 --> 00:12:21.620
I know.

00:12:21.620 --> 00:12:27.540
Brandon on the audience says, there should be a meta awesome list, like an awesome list of awesome lists.

00:12:27.540 --> 00:12:28.660
I'm sure there is.

00:12:28.660 --> 00:12:32.180
There is.

00:12:32.180 --> 00:12:32.800
I'm sure.

00:12:32.800 --> 00:12:35.320
And yeah, quite fun.

00:12:35.320 --> 00:12:37.260
I definitely recommend people check that out.

00:12:37.260 --> 00:12:38.180
All right.

00:12:38.760 --> 00:12:40.040
Dean, that brings us to your first item.

00:12:40.040 --> 00:12:40.680
Tell us about it.

00:12:40.680 --> 00:12:41.240
Yeah.

00:12:41.240 --> 00:12:45.360
So at work, I needed to write something that required threading.

00:12:45.360 --> 00:12:48.160
And I was very afraid of threading at the beginning.

00:12:48.160 --> 00:12:51.560
Basically, what we needed to do, we have some mechanism.

00:12:51.820 --> 00:13:02.240
I'm a data scientist and we need to take many queries at once and get them as Pandas data frames and save them to disk and later take all of them and work with them.

00:13:02.240 --> 00:13:07.740
And instead of writing, like sending them sequentially, I wanted to send a bunch of them together.

00:13:08.220 --> 00:13:19.840
And the bonus thing I found that is that when you release them to a threading, if you don't lock the threads or you don't wait for the threads, you can actually still work with the Jupyter notebook while waiting for the queries.

00:13:20.020 --> 00:13:21.860
So that was my main reasoning.

00:13:21.860 --> 00:13:30.500
And eventually, after I've written most of the code, I got this blog post called The Threadpool Executor in Python, The Complete Guide.

00:13:30.500 --> 00:13:32.740
So this is basically Jason Brownlee.

00:13:32.740 --> 00:13:38.060
He's a guy who's also the guy from Machine Learning Mastery, so I'm very familiar with him.

00:13:39.820 --> 00:13:49.680
It's a very long blog post, so you could kind of read it as an e-book or just access the stuff you need because it's like, I don't know, a two-hour read maybe.

00:13:49.680 --> 00:13:53.700
And he explains everything from the beginning.

00:13:53.700 --> 00:13:57.500
He explains what are Python threads, how to work with them.

00:13:57.500 --> 00:14:02.820
Then he introduces the Threadpool Executor, which is a more convenient way to use threads.

00:14:02.820 --> 00:14:10.980
He explains about the lifecycle of what does he do, how to do it then with a context manager and stuff like that.

00:14:10.980 --> 00:14:22.440
And eventually, what he talks about that other people do not when you search for a threading tutorial is actually about the complete lifecycle and then the usage patterns.

00:14:22.440 --> 00:14:26.640
And then he explains about IO bound versus CPU bound and everything.

00:14:26.640 --> 00:14:29.820
And he finishes off with the common questions.

00:14:29.820 --> 00:14:33.860
So this is like the link I've saved because I will forget it in a week.

00:14:33.860 --> 00:14:39.660
But the next time I need to, I just know I can come back to this and like read the common questions part.

00:14:39.660 --> 00:14:41.540
And yes, there are questions.

00:14:41.540 --> 00:14:44.060
The questions like, how do you stop running?

00:14:44.060 --> 00:14:44.720
There's a lot there, yeah.

00:14:44.720 --> 00:14:47.980
There is a lot there in this article, isn't there?

00:14:47.980 --> 00:14:48.580
Yeah, it's a lot.

00:14:48.580 --> 00:14:49.160
It's a lot.

00:14:49.160 --> 00:14:52.940
But the thing is, you can come back later and just take the stuff you need.

00:14:52.940 --> 00:14:57.840
Like I remember, I know I'm working, then I can ask myself, how do you set a chunk size in map?

00:14:57.840 --> 00:15:00.800
Well, it says there that you don't because that's for the process pool.

00:15:00.800 --> 00:15:02.300
But then I have another question.

00:15:02.300 --> 00:15:03.980
Maybe how do you cancel a running test?

00:15:03.980 --> 00:15:04.880
And the answer is that.

00:15:04.880 --> 00:15:11.720
So I think that's a good thing to have like to quickly access when you need to.

00:15:11.720 --> 00:15:19.880
And it finishes off with like, what's the difference from asyncIO, from threading.thread, from process pool executor.

00:15:19.880 --> 00:15:23.860
So that is a very helpful guide, very complete.

00:15:23.860 --> 00:15:33.660
And the entire blog actually explains, like it's an entire blog dedicated to the threading pool executor and the process pool executor.

00:15:33.660 --> 00:15:43.600
I love that it's covering the thread pool and process pools because it's easy for things to just completely get out of control.

00:15:43.600 --> 00:15:47.000
You know, as you throw more work at it, stuff can completely back up.

00:15:47.000 --> 00:15:49.680
So if you just say, create me a new thread and run that.

00:15:49.680 --> 00:15:51.220
And then another place, create me more threads.

00:15:51.220 --> 00:15:52.060
And I got a bunch more.

00:15:52.060 --> 00:15:54.200
Oh, look, now I have a thousand items to process.

00:15:54.200 --> 00:15:55.320
Create a thousand threads.

00:15:55.320 --> 00:15:55.580
Yeah.

00:15:55.580 --> 00:15:59.240
Each thread takes a lot of context switching to switch between.

00:15:59.240 --> 00:16:01.980
And they take a decent amount of memory and all sorts of stuff, right?

00:16:02.520 --> 00:16:05.700
Through the thread pool, you can say, queue up the work and run 10 at a time.

00:16:05.700 --> 00:16:12.580
Same for processes, which sort of sets an upper bound on how much concurrency you can deal with, right?

00:16:12.580 --> 00:16:12.960
Yeah.

00:16:12.960 --> 00:16:14.240
Yeah.

00:16:14.240 --> 00:16:15.200
This is cool.

00:16:15.200 --> 00:16:19.100
So you talked about solving some problems in Jupyter Notebook using this.

00:16:19.100 --> 00:16:20.900
What in particular were you trying to do?

00:16:20.900 --> 00:16:25.760
So basically, I can send, I know, a thousand queries.

00:16:25.760 --> 00:16:31.000
And once they get, like, we have big data and then they have a query that takes a part of it.

00:16:31.380 --> 00:16:35.720
Like, after maybe some group buys and limitations and stuff like that.

00:16:35.720 --> 00:16:38.340
And I want to take the data frame and save it.

00:16:38.340 --> 00:16:38.980
Right.

00:16:39.420 --> 00:16:48.260
And then once I have the entire data from all the queries, I want to join them or maybe do some, I don't know, some processing and then join everything.

00:16:48.260 --> 00:16:59.220
The thing is, after, like, 10 of those came back, I have a sample of my data that I can work with and try to manage and then have a code written.

00:16:59.220 --> 00:17:03.600
And while the other stuff is still written, I want to have that, like, I can play with it.

00:17:04.100 --> 00:17:13.520
So if I release the other things to the threads and they work in the background, the main thread of the Jupyter Notebook is open.

00:17:13.520 --> 00:17:16.360
And you can start working on the same notebook.

00:17:16.360 --> 00:17:27.620
Before then, I used to, like, open a notebook that's querying stuff, open a notebook that I'm playing with and, like, see that the file paths are the same.

00:17:27.620 --> 00:17:32.220
So I'm not confused with, like, some other directory of the other versioning of this data.

00:17:32.220 --> 00:17:34.280
And now it just works.

00:17:34.700 --> 00:17:35.300
Oh, that's really cool.

00:17:35.300 --> 00:17:43.460
And you can also, like, add a thread for, I know, with some visualizations of what's finished, what's eroded, what's, like, everything.

00:17:43.460 --> 00:17:44.240
Fantastic.

00:17:44.240 --> 00:17:45.860
Yeah, that sounds really good.

00:17:45.860 --> 00:17:49.000
I'm sure there's a lot of concurrency and parallelism in the data backend.

00:17:49.000 --> 00:17:52.340
It's just how do you sort of access that from Python, right?

00:17:52.340 --> 00:17:53.980
So how do you issue all those commands?

00:17:53.980 --> 00:17:54.720
Excellent.

00:17:54.720 --> 00:17:55.560
All right.

00:17:55.560 --> 00:17:56.960
Let's see.

00:17:56.960 --> 00:17:59.840
Brian, anything you want to add before I talk about Datadog?

00:17:59.840 --> 00:18:00.920
No.

00:18:01.920 --> 00:18:09.720
Some comments, like, Sam, Morley, concurrent futures is a much less painful way to work with them at a higher level.

00:18:09.720 --> 00:18:14.820
So maybe we could get an article on concurrent futures on the upside sometimes.

00:18:14.820 --> 00:18:15.820
Yeah, for sure.

00:18:15.820 --> 00:18:20.780
So the thread pool executor gets you back futures.

00:18:20.780 --> 00:18:30.720
And then part of what's explained in the blog post is how to work with futures, like, as completed or sequentially or, like, you decide your strategy,

00:18:30.720 --> 00:18:32.400
but you work with the futures.

00:18:32.400 --> 00:18:33.140
Nice.

00:18:33.140 --> 00:18:33.880
Okay.

00:18:33.880 --> 00:18:34.420
Cool.

00:18:34.420 --> 00:18:35.060
Yeah.

00:18:35.060 --> 00:18:35.420
Nice.

00:18:35.420 --> 00:18:41.780
And, of course, requisite shout out to Unsync, which is all sorts of awesome for this stuff.

00:18:41.780 --> 00:18:48.980
Unifies the API for direct threads for processes and AsyncIO.

00:18:49.520 --> 00:18:52.900
But what I want to tell you all about now is Datadog.

00:18:52.900 --> 00:18:54.020
Datadog is really awesome.

00:18:54.020 --> 00:18:56.320
You should really have insight into your applications.

00:18:56.320 --> 00:18:58.580
And that's what Datadog brings you.

00:18:58.580 --> 00:19:05.600
So Datadog is a real-time monitoring that unifies metrics, traces, logs into one tightly integrated platform.

00:19:06.360 --> 00:19:13.300
Their APM empowers developers to identify anomalies and resolve issues, especially around performance.

00:19:13.300 --> 00:19:22.700
You can begin collecting stack traces, visualize them as flame graphs, and organizing them into profile types, such as CPU bound, IO bound, and so on.

00:19:22.860 --> 00:19:35.080
And teams can even search specific profiles and correlate them to distributed traces to, you know, find things across different parts of your infrastructure and microservices and identify slow or underperforming code and then make it faster.

00:19:35.460 --> 00:19:37.860
Plus, you can use their APM live search.

00:19:37.860 --> 00:19:42.000
You can search across the full stream of all the traces over the last 15 minutes.

00:19:42.000 --> 00:19:46.440
So try Datadog APM for free with a 14-day trial.

00:19:46.440 --> 00:19:52.600
And then Datadog will send you one of these very cute doggy t-shirts, which who wouldn't want one of those, right?

00:19:52.600 --> 00:19:58.240
So visit high-dom-by-stud.fm slash Datadog or just click the link in your podcast player show notes to get started.

00:19:58.240 --> 00:19:59.140
Thanks, Datadog.

00:19:59.140 --> 00:20:01.580
And, Brian, back to you.

00:20:01.580 --> 00:20:02.380
Back to me.

00:20:02.380 --> 00:20:15.020
I was, I'm going to apologize whoever tweeted this, but somebody tweeted this out, a link to this article, and talking about chaining operators.

00:20:15.020 --> 00:20:19.300
So this is an article by Rodrigo Serrao.

00:20:19.300 --> 00:20:21.920
Py don'ts?

00:20:21.920 --> 00:20:23.220
Yeah.

00:20:23.220 --> 00:20:28.260
So I don't know what the py don'ts are about.

00:20:28.260 --> 00:20:29.360
Just, I don't know.

00:20:29.360 --> 00:20:31.860
Maybe he started blogging about things you shouldn't do in Python.

00:20:32.160 --> 00:20:38.260
Anyway, this article is called Chaining Comparison Operators, and I use chaining all the time.

00:20:38.260 --> 00:20:43.280
Mostly, I use it for simple things like, oh, let me find one.

00:20:43.280 --> 00:20:45.620
A is less than B, less than C.

00:20:45.620 --> 00:20:50.240
So ranges, like min, you know, my X value is between min and max.

00:20:50.240 --> 00:20:51.540
Yeah, that's really nice.

00:20:51.540 --> 00:20:51.760
Yeah.

00:20:51.760 --> 00:20:58.640
My hint on that, like, just tip for anybody doing that, always do them less than.

00:20:58.640 --> 00:21:01.840
Don't do greater than, because it's hard to do that.

00:21:01.840 --> 00:21:04.460
Anyway, so keep them like that.

00:21:04.460 --> 00:21:06.480
But this article is talking about other stuff.

00:21:06.480 --> 00:21:10.320
So this is pretty easy to think about, like the less than operators.

00:21:10.320 --> 00:21:12.460
So A is less than B, less than C.

00:21:12.460 --> 00:21:18.200
Is that really the same as A is less than B and B is less than C.

00:21:18.200 --> 00:21:19.420
It is that combination.

00:21:19.580 --> 00:21:21.380
That's what chained operators are.

00:21:21.380 --> 00:21:28.140
And the importance there is it doesn't really work for some operations.

00:21:28.140 --> 00:21:31.660
And it gets into, like, the equal operator.

00:21:31.660 --> 00:21:36.240
So you can do A equals B or equals C, which means they're all equal.

00:21:36.240 --> 00:21:36.820
Great.

00:21:36.820 --> 00:21:38.100
What about not equal?

00:21:38.100 --> 00:21:39.600
Does that work the same way?

00:21:40.560 --> 00:21:52.540
And it doesn't, because if you've got, like, A is not equal to B is not equal to C, it doesn't mean they're all different, because A and C still could be the same and have that pass.

00:21:53.080 --> 00:22:07.040
So this article, if you're working with chained expressions, which I think you should, if you're doing complicated things, it's way, I like it better than doing, having a bunch of ands in there, as long as that you can keep it readable.

00:22:07.040 --> 00:22:17.800
But this article talks through some of the, some of the gotchas inside of, and things to watch out for, like side effects and non-constants and things like that.

00:22:17.800 --> 00:22:20.720
So great discussion of chained operators.

00:22:21.340 --> 00:22:23.200
I hadn't even thought of doing this not equal to.

00:22:23.200 --> 00:22:23.960
This seems wrong.

00:22:23.960 --> 00:22:25.720
It just looks wrong.

00:22:25.720 --> 00:22:27.700
Yeah.

00:22:27.700 --> 00:22:31.440
But, yeah, don't do chained not equal.

00:22:31.440 --> 00:22:39.500
That's just, unless, and even if that's what you meant, that, like, A is not equal to B and B is not equal to C, but it's okay for A and C to be equal.

00:22:39.500 --> 00:22:41.980
That would be a terrible expression, because it's confusing.

00:22:41.980 --> 00:22:42.760
So don't do that.

00:22:42.760 --> 00:22:43.240
It is.

00:22:43.240 --> 00:22:43.640
Yeah.

00:22:43.640 --> 00:22:50.560
My favorite one of these chainings, like, X, you know, 7 less than X less than 10.

00:22:50.560 --> 00:22:50.960
Yeah.

00:22:51.040 --> 00:22:51.560
Something like that.

00:22:51.560 --> 00:22:51.980
That's nice.

00:22:51.980 --> 00:22:58.920
My favorite is converting X, if X is not none, else Y to just X or Y.

00:22:58.920 --> 00:22:59.620
Boom.

00:22:59.620 --> 00:23:01.880
That's so clean and so nice.

00:23:01.880 --> 00:23:07.180
And I never, coming from a C++ background and C#, I never thought that was possible.

00:23:07.180 --> 00:23:07.760
That's great.

00:23:07.760 --> 00:23:08.380
Yeah.

00:23:09.100 --> 00:23:10.180
Dean, what do you think about this?

00:23:10.180 --> 00:23:11.600
I love it.

00:23:11.600 --> 00:23:13.140
I use it a lot.

00:23:13.140 --> 00:23:15.260
It didn't always work.

00:23:15.260 --> 00:23:20.180
I think it's still not working with Pandas data frames or Pandas series and arrays.

00:23:20.720 --> 00:23:24.380
And I do wait for this to finally work.

00:23:24.380 --> 00:23:30.860
Arrays, when you do an array, like in NumPy or Pandas, when you do an array, it's less than some number.

00:23:31.240 --> 00:23:35.320
It returns a new array with true, like a Boolean array with true and false.

00:23:35.320 --> 00:23:40.640
And last time I checked was a few months ago, but the last time I checked, it didn't work.

00:23:40.640 --> 00:23:46.520
I couldn't do one is less than the series is less than two and get the Boolean array.

00:23:46.980 --> 00:23:50.800
So I'm waiting for this, but I love the concept a lot.

00:23:50.800 --> 00:23:51.640
Okay.

00:23:51.640 --> 00:23:52.120
That's good.

00:23:52.120 --> 00:23:52.600
Yeah.

00:23:52.600 --> 00:23:55.060
I had to consider the integration into Pandas.

00:23:55.060 --> 00:23:55.440
Yeah.

00:23:55.440 --> 00:23:56.100
But of course.

00:23:56.100 --> 00:24:03.860
I'm not sure how would you implement that with the regular data model of like Dunder, Dunder EQ or is this something else?

00:24:03.860 --> 00:24:04.340
I'm not sure.

00:24:04.340 --> 00:24:04.800
LTE.

00:24:04.800 --> 00:24:05.240
LTE.

00:24:05.240 --> 00:24:05.640
Yeah.

00:24:05.640 --> 00:24:06.120
Possibly.

00:24:06.120 --> 00:24:06.800
I'm not sure either.

00:24:06.800 --> 00:24:08.020
Yeah.

00:24:08.020 --> 00:24:15.120
There's probably some magic method and it might just expand out to less than and then and, you know, like the two tests basically.

00:24:15.120 --> 00:24:15.860
Probably does.

00:24:15.860 --> 00:24:16.840
Oh, cool.

00:24:16.900 --> 00:24:23.020
We should ask Brett Cannon to do a deep dive into what he changed off.

00:24:23.020 --> 00:24:26.740
He's pulling apart all the different parts of Python syntax, right?

00:24:26.740 --> 00:24:26.920
Yeah.

00:24:26.920 --> 00:24:27.600
All right.

00:24:27.600 --> 00:24:32.980
I want to give a quick shout out to Rich because it's one of our episodes.

00:24:32.980 --> 00:24:34.120
So we talk about Rich.

00:24:34.120 --> 00:24:37.320
I was going to talk about Anthony Shaw, but I didn't have enough information.

00:24:37.320 --> 00:24:41.240
So, I mean, he's the other person who needs a shout out in every show.

00:24:41.300 --> 00:24:48.060
So I want to talk about this article highlighting some tools by Martin Hines.

00:24:48.140 --> 00:24:48.540
Yeah.

00:24:48.540 --> 00:24:49.100
Martin Hines.

00:24:49.100 --> 00:24:49.700
Yeah.

00:24:49.700 --> 00:24:53.200
Well, creating beautiful tracebacks with Python, done exception hooks.

00:24:53.200 --> 00:24:55.660
So two things that I want to point out here.

00:24:55.660 --> 00:24:59.640
One, Python has an exception hook mechanism, which is pretty cool.

00:24:59.720 --> 00:25:08.440
So what you can do is you can create a function that has this signature of exception type, the actual exception and the traceback.

00:25:08.440 --> 00:25:09.700
So three arguments.

00:25:09.960 --> 00:25:19.380
And if you have a function like that, you can just go to the sys and say sys.accept hook equals that function, not calling it, of course, just passing the function as the value.

00:25:19.380 --> 00:25:22.460
And then whenever there's an exception, this will be called by Python.

00:25:22.460 --> 00:25:23.380
That's pretty cool, right?

00:25:23.380 --> 00:25:24.200
Yeah.

00:25:24.200 --> 00:25:28.580
So depending on what you want to do, like you could say, well, we're going to store all the errors.

00:25:28.580 --> 00:25:32.640
Like, let's imagine here's a scenario where you might make use of this.

00:25:33.020 --> 00:25:35.920
I'm going to create an app and I'm going to send it out.

00:25:35.920 --> 00:25:40.480
I'm going to use Py2App or Py2XE or just, you know, let people install it somehow.

00:25:40.480 --> 00:25:49.600
And then when it runs, I want to, it's going to run on their computers, but I want to gather up all the exceptions of all the users across the company or the research team or whatever.

00:25:49.600 --> 00:25:55.920
You could have this, submit this error along with other details right back to a database over an API, right?

00:25:55.920 --> 00:25:59.760
And then you could do like analytics, like, well, here's the most common error and so on.

00:25:59.820 --> 00:26:05.660
Of course, you could use Sentry or something like that, but maybe you're trying to gather some specific information that's different, right?

00:26:05.660 --> 00:26:07.960
So that's one of the types of things you could do with this.

00:26:07.960 --> 00:26:09.620
So I got a question before I go on.

00:26:09.620 --> 00:26:10.420
Yeah.

00:26:10.420 --> 00:26:13.600
So this doesn't catch the exception.

00:26:13.600 --> 00:26:16.400
It just, it doesn't interrupt the flow.

00:26:16.400 --> 00:26:18.860
It just, it just gets called when it happens.

00:26:18.860 --> 00:26:22.380
It doesn't, it doesn't catch the exception.

00:26:22.380 --> 00:26:27.560
It lets you basically change what kind of output comes from Python.

00:26:27.560 --> 00:26:32.600
So if you just wanted to print out like, here's a file where there was an error and here's the error message.

00:26:32.600 --> 00:26:33.080
Okay.

00:26:33.080 --> 00:26:34.480
Like you could do that, right?

00:26:34.480 --> 00:26:36.420
Or the type and then the message.

00:26:36.420 --> 00:26:39.360
I'm just noting the, noticing the example doesn't re-throw it.

00:26:39.360 --> 00:26:41.760
So you don't have to do that then.

00:26:41.760 --> 00:26:42.860
No, I don't believe so.

00:26:42.860 --> 00:26:44.080
And I'm not a hundred percent sure.

00:26:44.080 --> 00:26:47.180
I think the app, I think the process still ends.

00:26:47.400 --> 00:26:51.540
If it's just a regular running script rather than a web app.

00:26:51.540 --> 00:26:53.040
I think it still ends.

00:26:53.040 --> 00:26:54.940
But anyway, sorry.

00:26:54.940 --> 00:26:55.860
You get a different kind of output.

00:26:55.860 --> 00:26:56.240
Yeah, yeah.

00:26:56.240 --> 00:26:59.860
No, you just don't get the standard print output that Python gives you, right?

00:26:59.860 --> 00:27:02.680
So you could say, avoid printing the trace back if you wanted.

00:27:02.680 --> 00:27:04.720
You could just say this file on this line had this error.

00:27:04.720 --> 00:27:05.600
Oh, yeah.

00:27:05.600 --> 00:27:05.960
Okay.

00:27:05.960 --> 00:27:06.340
Nice.

00:27:06.340 --> 00:27:06.700
Okay.

00:27:06.700 --> 00:27:09.700
So it's easy enough to do.

00:27:09.700 --> 00:27:13.920
Like, for example, they have this function that they call that caused an error.

00:27:13.920 --> 00:27:17.940
And all you see when this crashes is there's a trace back.

00:27:17.940 --> 00:27:22.540
This file, this line in this module, here's the error message, right?

00:27:22.540 --> 00:27:25.220
Instead of the huge stack trace that might scare people.

00:27:25.220 --> 00:27:25.800
Okay.

00:27:25.800 --> 00:27:29.580
So, I mean, obviously you can use try and accept, but this is global, right?

00:27:29.580 --> 00:27:33.760
So even if some library is calling something and you're not catching it and like, right,

00:27:33.760 --> 00:27:35.180
it's catching everywhere.

00:27:35.180 --> 00:27:35.780
Okay.

00:27:35.780 --> 00:27:38.180
So then you could do more work about breaking that apart.

00:27:38.180 --> 00:27:39.760
And they talk about doing that.

00:27:39.760 --> 00:27:44.180
But the real interesting part is if you go and look at some options.

00:27:44.180 --> 00:27:48.160
So there are five, I believe there are five libraries mentioned in here that do really cool

00:27:48.160 --> 00:27:49.440
stuff for solving this.

00:27:49.440 --> 00:27:53.720
The first one is by Will McGugan's rich library.

00:27:53.720 --> 00:27:58.840
So you can just go from rich.traceback, import install, and then say install.

00:27:58.840 --> 00:28:00.100
Show locals is true.

00:28:00.100 --> 00:28:04.300
And then this also basically installs one of those global exception hooks.

00:28:05.220 --> 00:28:11.020
With the benefit being when you get the errors, what you get is a nice rich output.

00:28:11.020 --> 00:28:12.100
It's super pretty.

00:28:12.100 --> 00:28:14.260
It's pretty and it's useful.

00:28:14.260 --> 00:28:18.360
I mean, it's color highlighted so you can see where the error happened, but it also will

00:28:18.360 --> 00:28:23.200
print out in a really nice way with formatting and highlighting the locals, right?

00:28:23.580 --> 00:28:26.980
So what values were passed to that function when it's crashed?

00:28:26.980 --> 00:28:29.480
Well, here's a little table of those and so on.

00:28:29.480 --> 00:28:32.480
So this is really easy to identify.

00:28:32.480 --> 00:28:35.420
And at the very bottom, like a nice clear way to like, okay, what happened?

00:28:35.420 --> 00:28:38.320
So you can do this super simple version here.

00:28:38.320 --> 00:28:41.500
And there's also some manual ways to make rich print this type of stuff.

00:28:42.300 --> 00:28:46.960
Number two is better exceptions, which does similar stuff.

00:28:46.960 --> 00:28:51.540
You can see that it doesn't quite take over how the look and feel is so much, but it basically

00:28:51.540 --> 00:28:55.400
colorizes the standard look and feel of errors.

00:28:55.400 --> 00:28:59.000
So you can see, you know, which function, which error and so on.

00:28:59.000 --> 00:28:59.660
So that's pretty good.

00:28:59.660 --> 00:29:01.100
And there's pretty errors.

00:29:01.100 --> 00:29:02.460
Check out pretty errors.

00:29:02.460 --> 00:29:04.480
This looks pretty good, right?

00:29:05.100 --> 00:29:07.000
It's got a lot of like bold and highlights.

00:29:07.000 --> 00:29:11.740
You can really call out the error messages and the functions involved in the modules involved.

00:29:11.740 --> 00:29:13.060
Here's one for you, Dean.

00:29:13.060 --> 00:29:14.980
The built-in one to IPython.

00:29:14.980 --> 00:29:19.880
It has ultra TV for ultra traceback.

00:29:19.880 --> 00:29:21.420
And this is pretty nice, right?

00:29:21.420 --> 00:29:23.320
Actually, the IPython one's pretty good.

00:29:23.320 --> 00:29:26.080
Yeah, the Python one is really nice.

00:29:26.080 --> 00:29:31.040
And also I was planning to talk about it in the extras, but on IPython 8, which is pretty

00:29:31.040 --> 00:29:38.100
new, they even have this improved with some color coloring of exactly where the error happened.

00:29:38.100 --> 00:29:42.000
I think this uses the 310 part or something like that.

00:29:42.000 --> 00:29:42.960
Oh, awesome.

00:29:42.960 --> 00:29:43.620
Yeah, that's cool.

00:29:43.620 --> 00:29:47.020
We'll hear more about it when we talk about IPython 8 as well.

00:29:47.020 --> 00:29:47.540
Cool.

00:29:47.540 --> 00:29:51.500
Yeah, so that's built in, kind of if you're already on the data science stack.

00:29:51.500 --> 00:29:57.240
And then finally, stack printer, which you can give it a traceback and it will print that

00:29:57.240 --> 00:29:57.340
out.

00:29:57.340 --> 00:30:01.320
So you can sort of do like rich, you can say set exception hook and give it a theme

00:30:01.320 --> 00:30:02.760
like dark or whatever.

00:30:02.760 --> 00:30:05.880
And then it does this pretty nice printout as well.

00:30:05.880 --> 00:30:06.980
So these are all great.

00:30:06.980 --> 00:30:13.000
I'm personally liking the rich tracebacks version best, but this is really nice.

00:30:13.000 --> 00:30:13.260
Yeah.

00:30:13.260 --> 00:30:18.640
Connor out there in the audience says, wow, using show local SQL tree would have saved me hours

00:30:18.640 --> 00:30:19.540
and hours of time.

00:30:19.540 --> 00:30:20.500
And you and me both.

00:30:20.500 --> 00:30:20.800
Yeah.

00:30:20.800 --> 00:30:21.820
I feel the pain.

00:30:21.820 --> 00:30:23.480
I do too.

00:30:23.480 --> 00:30:27.880
So because a lot of times you're like, I know it crashed and it says none type does

00:30:27.880 --> 00:30:29.260
not have attribute, whatever.

00:30:29.260 --> 00:30:30.940
But like, why is it none?

00:30:30.940 --> 00:30:32.580
I need to go back three levels, right?

00:30:32.580 --> 00:30:34.480
Like, yes, it's so good.

00:30:34.480 --> 00:30:38.480
And then you find out you just forgot to return from the function.

00:30:38.480 --> 00:30:40.400
Yes, exactly.

00:30:41.340 --> 00:30:48.920
I was just debugging a test failure the other day and pytest has the option to throw a local.

00:30:48.920 --> 00:30:54.340
You can show locals with a crash or with every failure.

00:30:54.340 --> 00:31:03.560
And I forgot that the particular thing I was testing had like variables that were storing

00:31:03.560 --> 00:31:05.940
thousand element arrays.

00:31:06.900 --> 00:31:08.500
It just like went on for me.

00:31:08.500 --> 00:31:16.820
I believe the, I believe Rich's has a truncate variables where it'll do an ellipsis or something

00:31:16.820 --> 00:31:17.220
like that.

00:31:17.220 --> 00:31:18.000
I think.

00:31:18.000 --> 00:31:21.560
I mean, yeah, I'm not a hundred percent sure because I've been looking at all five of these

00:31:21.560 --> 00:31:22.320
Will's in the chat.

00:31:22.320 --> 00:31:22.960
We'll have to ask him.

00:31:22.960 --> 00:31:23.760
Will's in the chat.

00:31:23.760 --> 00:31:24.960
You'll have to give us a shout out.

00:31:24.960 --> 00:31:27.260
Will, I think, I think truncate is out there, right?

00:31:27.260 --> 00:31:28.400
I'm not a hundred percent sure.

00:31:28.400 --> 00:31:34.140
I think of how can I actually, so I talk with databases and sometimes the errors from the

00:31:34.140 --> 00:31:38.760
databases are like this big Java stack, Java trace.

00:31:38.760 --> 00:31:43.420
And then you need to like a lot of go a lot of apps.

00:31:43.420 --> 00:31:45.760
Sorry, something, some noise here.

00:31:45.760 --> 00:31:46.240
Sorry.

00:31:46.240 --> 00:31:51.340
You need to go a lot, a lot up in the browser to actually see the error.

00:31:51.580 --> 00:31:55.720
And if I could just shut it down and just give me the Python stuff.

00:31:55.720 --> 00:31:57.200
Cool.

00:31:57.200 --> 00:31:57.440
Yeah.

00:31:57.440 --> 00:32:02.440
I don't know what setting you set for that, but certainly with this mechanism, you could

00:32:02.440 --> 00:32:06.000
set it up so that like if the word Java appears, you just stop.

00:32:06.000 --> 00:32:06.680
Yeah.

00:32:06.680 --> 00:32:08.240
You just stop going back.

00:32:08.240 --> 00:32:08.820
Yeah.

00:32:08.820 --> 00:32:10.740
And Will says, yes, that's right.

00:32:10.740 --> 00:32:12.160
Thank you, Brian, for pulling that up.

00:32:12.160 --> 00:32:12.340
Yeah.

00:32:12.340 --> 00:32:12.640
Yeah.

00:32:12.640 --> 00:32:17.020
You can truncate it so that printing won't go completely insane because it could be gigabytes.

00:32:17.020 --> 00:32:18.880
I mean, it could be out of control, right?

00:32:18.880 --> 00:32:19.500
Yeah.

00:32:19.500 --> 00:32:19.540
Yeah.

00:32:19.540 --> 00:32:24.780
But I mean, even if they have a like reasonably large limit, sometimes it's just like, oh,

00:32:24.780 --> 00:32:28.160
I forgot that huge array was there and it's hard to see stuff.

00:32:28.160 --> 00:32:28.400
So.

00:32:28.400 --> 00:32:29.580
Yeah, absolutely.

00:32:29.580 --> 00:32:30.360
Yeah.

00:32:30.360 --> 00:32:31.540
All right.

00:32:31.540 --> 00:32:32.360
Over to you, Dean.

00:32:32.360 --> 00:32:37.120
Speaking of testing, Brian was talking about testing stuff and looking at the color and so

00:32:37.120 --> 00:32:37.240
on.

00:32:37.240 --> 00:32:37.420
Yeah.

00:32:37.420 --> 00:32:39.660
So I thought, Brian, this would be up your alley.

00:32:39.660 --> 00:32:44.400
So it's called Ways I Use Testing as a Data Scientist.

00:32:44.400 --> 00:32:46.160
It's by Peter Baumgartner.

00:32:46.800 --> 00:32:50.740
And I'm a data scientist, but I also love testing.

00:32:50.740 --> 00:32:56.780
The thing about testing with data science is sometimes it's not that clear what you should

00:32:56.780 --> 00:32:57.740
test for.

00:32:57.740 --> 00:32:58.320
Right.

00:32:58.380 --> 00:33:05.420
because sometimes some things we do are stochastic and then you could not actually test for stuff or stuff like that.

00:33:06.040 --> 00:33:14.820
So this blog talks about like the art of testing because sometimes like the it's not clear what you should test.

00:33:14.820 --> 00:33:18.760
And the more experience you get, you can actually see what's coming your way.

00:33:18.760 --> 00:33:27.420
And he talks about like data validation and he is throwing many packages that could help you.

00:33:27.420 --> 00:33:33.720
Packages like Pandera and Great Expectations that I think we've talked about before in the podcast.

00:33:34.200 --> 00:33:40.460
And also like the NumPy has some stuff like is closed.

00:33:40.460 --> 00:33:47.100
Checks for two numbers close to each other or array equal assert data frame equals in Pandas data frame.

00:33:47.100 --> 00:33:48.860
So he talks a lot about that.

00:33:48.860 --> 00:33:52.500
He also talks about using assert in your code.

00:33:52.500 --> 00:33:58.220
Like even if you had some ad hoc stuff of analysis, use assert within the code.

00:33:58.220 --> 00:33:59.840
Don't think about the tests later.

00:33:59.840 --> 00:34:03.460
Just think like where does this thing could hurt me?

00:34:03.460 --> 00:34:04.320
He gives an example.

00:34:05.200 --> 00:34:12.380
Maybe if I'm trying to join two data frames and I think they have the same shape, I want to check if they have the same IDs.

00:34:12.380 --> 00:34:16.040
So that way I know that the join works correctly.

00:34:16.040 --> 00:34:21.940
So he asserts that the length of the IDs is the same within the two data frames.

00:34:21.940 --> 00:34:25.300
And this is not even like real testing, we would say.

00:34:25.300 --> 00:34:27.160
It doesn't use some testing framework.

00:34:27.160 --> 00:34:30.080
It just says like write it within your code.

00:34:30.540 --> 00:34:40.800
It then continues to like hypothesis, which basically bombards the functions with a lot of ways to actually try to fail it.

00:34:40.800 --> 00:34:52.860
It continues with some other packages and eventually goes into a pytest and shows like how it would work with pytest and with like an approach that I haven't heard of.

00:34:52.860 --> 00:34:54.860
But it sounds good.

00:34:54.860 --> 00:34:57.980
Arrange, act, assert.

00:34:58.480 --> 00:35:08.920
Like arrange the data, then act on those things you want to check and then just assert if they are equal or almost equal and the thing you wanted to check for.

00:35:08.920 --> 00:35:13.360
Yeah, it's such a such a easy mistake to make.

00:35:13.360 --> 00:35:15.380
Like this number equal equal that number.

00:35:15.380 --> 00:35:15.980
Yep.

00:35:15.980 --> 00:35:18.800
And it's what when you do in science or data science.

00:35:19.540 --> 00:35:29.020
I'm glad he talks about structure because a lot of people that get into testing get get these giant tests that do do a little work, test something, do a little more work, test something.

00:35:29.020 --> 00:35:33.580
And then if it breaks, you're not sure what where the failure is.

00:35:33.580 --> 00:35:36.320
So this looks sounds fascinating.

00:35:36.320 --> 00:35:42.300
And actually, I'm not sure how I missed it, but I really want a way to compare an array for almost equal.

00:35:42.580 --> 00:35:45.060
So I'm going to have to go read that.

00:35:45.060 --> 00:35:49.780
Yeah, so NumPy and Pandas both have mechanisms for that.

00:35:49.780 --> 00:35:50.460
It's pretty great.

00:35:50.460 --> 00:35:50.980
Nice.

00:35:50.980 --> 00:35:51.500
Cool.

00:35:51.500 --> 00:35:52.580
Yeah.

00:35:52.580 --> 00:35:53.760
Very nice.

00:35:53.760 --> 00:35:55.520
I know this will be helpful to people.

00:35:55.520 --> 00:36:03.880
It's really I always wonder about testing data science stuff and machine learning things and so on where you get small perturbations, but they're fine.

00:36:04.020 --> 00:36:10.200
It's off by one millionth of some unit, but that's totally good.

00:36:10.200 --> 00:36:10.680
Those are equal.

00:36:10.680 --> 00:36:14.220
But it takes, I think, an extra level of thinking about it.

00:36:14.220 --> 00:36:20.440
So much people focus on, well, how do you get rid of your dependencies and how do you make sure that you don't talk to the real database when you do this?

00:36:20.440 --> 00:36:23.520
And that's one aspect that people focus on.

00:36:23.520 --> 00:36:27.780
But this working with science-y type stuff is its own specialty.

00:36:27.960 --> 00:36:34.240
Yeah, I think the entire community is a fairly new community, although it's not as new as it was.

00:36:34.240 --> 00:36:40.100
And I'm not sure we're on top of how to do tests in machine learning.

00:36:40.100 --> 00:36:43.560
We have many packages for that.

00:36:43.560 --> 00:36:45.820
We have many theories for that.

00:36:45.820 --> 00:36:51.760
But I'm not sure we have actually one solid good way.

00:36:51.760 --> 00:36:52.800
And maybe we shouldn't have.

00:36:52.800 --> 00:36:54.640
But it's a debate.

00:36:54.640 --> 00:36:56.440
Yeah, for sure.

00:36:56.440 --> 00:36:58.580
Same with the rest of the software world.

00:36:58.580 --> 00:36:59.380
So welcome.

00:36:59.380 --> 00:37:02.380
Thanks.

00:37:02.380 --> 00:37:02.680
Yeah.

00:37:02.680 --> 00:37:07.700
And Sam out in the live stream says, NumPy has an assert array almost equal in NumPy.testing.

00:37:07.700 --> 00:37:08.500
Nice.

00:37:08.500 --> 00:37:10.580
I just learned there's a NumPy.testing.

00:37:10.580 --> 00:37:11.020
That's cool.

00:37:11.020 --> 00:37:11.680
Yeah.

00:37:11.680 --> 00:37:13.820
Awesome.

00:37:13.820 --> 00:37:14.200
All right.

00:37:14.200 --> 00:37:17.900
Dean, while you have your screen up, do you have any extras you want to talk about?

00:37:17.900 --> 00:37:19.920
I know IPython 8 was a thing.

00:37:19.920 --> 00:37:20.220
Yeah.

00:37:20.220 --> 00:37:27.240
So IPython 8 was released last month after three years of waiting for a major version.

00:37:28.200 --> 00:37:31.500
It has a lot of new features, but this is the extra part.

00:37:31.500 --> 00:37:32.460
So I won't go over them.

00:37:32.460 --> 00:37:35.320
Just two and a half things I wanted to mention.

00:37:35.320 --> 00:37:37.340
It says that it's less code.

00:37:37.340 --> 00:37:38.960
And I love that.

00:37:38.960 --> 00:37:42.620
Like once you get better in a programming language, you understand it.

00:37:42.620 --> 00:37:43.820
You shouldn't write more code.

00:37:43.880 --> 00:37:45.260
You should delete code.

00:37:45.260 --> 00:37:47.820
And that's what those guys do.

00:37:47.820 --> 00:37:56.240
And the way they could have done that is by hiring a person through the NumFocus small development grants.

00:37:56.240 --> 00:37:57.720
And I think this is important.

00:37:57.720 --> 00:38:02.160
It's actually been talked a lot about after the Log4j stuff.

00:38:02.160 --> 00:38:06.100
It's been talked about like, well, those are three guys who worked tirelessly.

00:38:06.100 --> 00:38:13.080
They have their full-time jobs and they couldn't fix the Log4j stuff maybe as quickly as some other people wanted.

00:38:13.080 --> 00:38:18.620
But then you realize that they got donations of like a few hundred dollars within 10 years.

00:38:18.620 --> 00:38:22.020
And then after the Log4j, suddenly they got a thousand.

00:38:22.020 --> 00:38:28.140
So this, I think it shows you how the money could help open source stuff.

00:38:28.140 --> 00:38:36.520
And maybe if you use some package in a company, in some corporate, maybe try and think how you can give back money.

00:38:36.520 --> 00:38:43.700
Or even if you give back code, if you free up your developers to actually contribute, this is awesome.

00:38:43.700 --> 00:38:49.120
And the half thing just mentioned because it talks about the traceback.

00:38:49.120 --> 00:38:53.480
It shows that you can now see it's like colored.

00:38:53.480 --> 00:39:00.180
You can see on the screen, it's called the part where actually the arrow was, it's colored now.

00:39:00.180 --> 00:39:02.720
So it's very nice to see.

00:39:02.720 --> 00:39:10.620
Like the example shows you, you add the function three times, but only it fails on just one input of them.

00:39:10.620 --> 00:39:13.600
So it shows you which of the three times the function failed.

00:39:13.860 --> 00:39:14.260
Right.

00:39:14.260 --> 00:39:19.140
You call it the same thing like foo of zero plus foo of one plus foo of two.

00:39:19.140 --> 00:39:26.360
And it's the middle one that failed, not just line seven, but the second invocation with the value one where it failed, which that's awesome.

00:39:26.360 --> 00:39:27.440
Yeah, exactly.

00:39:27.440 --> 00:39:28.660
And the same.

00:39:28.660 --> 00:39:29.640
Well, that's right.

00:39:29.640 --> 00:39:34.900
I was going to say the same thing for indexing into, what is that, a data frame or something like that.

00:39:34.900 --> 00:39:38.580
Like it's, you're chaining together like bracket zero, bracket one, bracket zero.

00:39:38.580 --> 00:39:42.300
It's the second one trying to get to the one of zero.

00:39:42.300 --> 00:39:43.520
That was the one that failed there.

00:39:43.580 --> 00:39:48.340
That's really, those are hard to come back and find if you're not in a debugger.

00:39:48.340 --> 00:39:49.580
Like, well, which one of these failed?

00:39:49.580 --> 00:39:50.140
Like, great.

00:39:50.140 --> 00:39:53.360
Array index out of bounds on line three.

00:39:53.360 --> 00:39:54.860
Well, there's three of those happening.

00:39:54.860 --> 00:39:55.380
Which one?

00:39:55.380 --> 00:39:56.660
Yeah.

00:39:56.660 --> 00:39:57.340
Yeah, that's cool.

00:39:57.340 --> 00:40:01.980
And another thing is a tweet by Victor Steiner is a core dev.

00:40:02.720 --> 00:40:08.120
And he says, I mean, it's now time to deprecate the standard lib, URL lib module.

00:40:08.640 --> 00:40:12.960
And this is brought a lot of haters and fans.

00:40:13.600 --> 00:40:15.900
And I'm not sure what's my opinion yet.

00:40:15.900 --> 00:40:18.200
I'm not a heavy user of URL lib.

00:40:19.160 --> 00:40:20.960
But it opened up a debate.

00:40:20.960 --> 00:40:22.660
Like, we know how to do.

00:40:23.920 --> 00:40:24.200
Yeah.

00:40:24.200 --> 00:40:24.840
Yeah.

00:40:24.840 --> 00:40:25.840
That's really interesting.

00:40:25.840 --> 00:40:28.420
There are certain things in the standard library.

00:40:28.420 --> 00:40:29.200
You're like, yeah, yeah.

00:40:29.200 --> 00:40:31.420
I know that's there and you could use it, but you probably shouldn't use it.

00:40:31.420 --> 00:40:36.400
There's like so many better external choices that are so good that it would be kind of silly

00:40:36.400 --> 00:40:37.240
to bite them.

00:40:37.240 --> 00:40:37.420
Right.

00:40:37.420 --> 00:40:38.720
That's sort of the recommendation here.

00:40:39.160 --> 00:40:41.960
Yeah, but also some people don't like it.

00:40:41.960 --> 00:40:45.940
They have people there that say they hate dependencies.

00:40:45.940 --> 00:40:50.980
And sometimes you can do most of the work with the standard lib.

00:40:50.980 --> 00:40:57.580
And some of the tweets said, like, maybe deprecate the major parts that requests can do.

00:40:57.580 --> 00:41:01.020
But there are some other parts that are actually really needed.

00:41:01.020 --> 00:41:03.120
So maybe deprecate half of it.

00:41:03.120 --> 00:41:04.680
Yeah.

00:41:04.680 --> 00:41:07.440
I'm not sure if I'm about deprecating it.

00:41:08.120 --> 00:41:11.420
But, you know, it's one thing to say there are better choices.

00:41:11.420 --> 00:41:14.820
And we as a community recommend you probably just don't use this.

00:41:14.820 --> 00:41:20.500
But to deprecate it means to people who would rather go with a dependence, a lower level of

00:41:20.500 --> 00:41:24.800
dependencies, you're giving them warnings that they shouldn't be doing this when maybe, you

00:41:24.800 --> 00:41:26.480
know, it's unlikely it's going to actually vanish.

00:41:26.480 --> 00:41:26.780
Right.

00:41:26.780 --> 00:41:32.800
There's like a fallacy, though, that I think some people have that if it's in this, if they

00:41:32.800 --> 00:41:36.300
don't have dependency and it's in the standard, they're using something in the standard library,

00:41:36.300 --> 00:41:37.340
it's more solid.

00:41:38.080 --> 00:41:43.780
But I don't know if there's that many people working on URL lib right now.

00:41:43.780 --> 00:41:49.500
And some of the other parts that maybe people want to stop supporting.

00:41:49.500 --> 00:41:51.400
That's something very valid.

00:41:51.400 --> 00:41:53.180
Python still is an open source project.

00:41:53.180 --> 00:41:55.620
And we can make those decisions.

00:41:55.620 --> 00:41:56.240
Yeah.

00:41:56.240 --> 00:42:00.780
Victor actually says there are 40-year-old security issues in URL lib.

00:42:01.120 --> 00:42:04.520
So maybe it's better to use something outside of it.

00:42:04.520 --> 00:42:05.260
Yeah.

00:42:05.260 --> 00:42:06.180
Yeah.

00:42:06.180 --> 00:42:07.200
People want it to stay.

00:42:07.200 --> 00:42:09.500
But there's these issues.

00:42:09.500 --> 00:42:10.260
Yeah.

00:42:10.260 --> 00:42:11.440
I wonder if there's a way to go.

00:42:11.440 --> 00:42:13.400
Well, let's look at some of the libraries that are out there.

00:42:13.400 --> 00:42:19.140
Try to bring them in and just use their core to replicate that functionality.

00:42:19.340 --> 00:42:21.640
Not to say, let's just pick on requests.

00:42:21.640 --> 00:42:23.000
Bring requests in.

00:42:23.000 --> 00:42:26.440
Vendor a little bit of it in so it does what URL lib does.

00:42:26.440 --> 00:42:28.060
And just go look.

00:42:28.060 --> 00:42:30.000
Okay, this is the latest, greatest that we got.

00:42:30.000 --> 00:42:32.040
And everyone's been looking at requests already.

00:42:32.040 --> 00:42:32.840
I don't know.

00:42:32.840 --> 00:42:33.800
It could be interesting.

00:42:33.800 --> 00:42:34.640
Yeah.

00:42:35.040 --> 00:42:39.420
And then Brandon out in the audience points out there are also maybe environments where

00:42:39.420 --> 00:42:41.500
you can't install dependencies for security reasons.

00:42:41.500 --> 00:42:46.660
And so having things like URL lib allows you to do more with Python in those situations.

00:42:46.660 --> 00:42:50.000
But if there's security problems with URL lib, yeah, anyway.

00:42:50.000 --> 00:42:53.140
Yeah, just in some of the functions, you don't call those.

00:42:53.140 --> 00:42:53.680
No, I'm just kidding.

00:42:53.680 --> 00:42:55.920
All right, Brian, how about you?

00:42:55.920 --> 00:42:56.300
Extras?

00:42:56.300 --> 00:42:57.860
Just one extra.

00:42:57.860 --> 00:43:00.580
I brought this up last week.

00:43:00.580 --> 00:43:01.960
I'm currently not writing a book.

00:43:01.960 --> 00:43:03.440
Yay!

00:43:03.440 --> 00:43:06.760
So I want to write more blog posts.

00:43:06.760 --> 00:43:12.940
So one of the things I wanted to make sure that my blog migrated to pythontest.com.

00:43:12.940 --> 00:43:16.040
And now it has a blog setting.

00:43:16.040 --> 00:43:18.840
And I like it.

00:43:18.840 --> 00:43:19.400
It looks pretty, too.

00:43:19.400 --> 00:43:25.660
Instead of just pulling everything over from my old WordPress blog, I'm trying to edit it.

00:43:25.660 --> 00:43:28.380
So I'm up through 2012.

00:43:29.220 --> 00:43:34.960
I'm going to go oldest to newest and gradually do things, bring things in.

00:43:34.960 --> 00:43:38.500
So that's one of my side projects I'm working on.

00:43:38.500 --> 00:43:39.580
Yeah, that's a great side project.

00:43:39.580 --> 00:43:40.540
Nice.

00:43:40.540 --> 00:43:41.960
What's that running on?

00:43:41.960 --> 00:43:45.400
Is that like some static site generator or other hosted thing?

00:43:45.400 --> 00:43:48.540
It's Hugo hosted by a free Netlify account.

00:43:48.540 --> 00:43:49.620
Yeah.

00:43:49.620 --> 00:43:50.740
Netlify is pretty awesome.

00:43:50.740 --> 00:43:51.880
All right.

00:43:51.980 --> 00:43:54.560
I got a couple of things I want to give a quick shout out to.

00:43:54.560 --> 00:43:55.340
Yeah, Brandon.

00:43:55.340 --> 00:43:58.260
Brandon had the same question, but we got it.

00:43:58.260 --> 00:43:58.720
All right.

00:43:58.720 --> 00:44:03.340
First of all, I have two new my Python shorts, two new versions, two videos from there.

00:44:03.340 --> 00:44:04.580
I got beyond the list comprehension.

00:44:04.580 --> 00:44:06.800
So basically set and dictionary comprehensions.

00:44:06.800 --> 00:44:07.560
Fun stuff.

00:44:07.560 --> 00:44:08.380
Nice picture.

00:44:08.380 --> 00:44:09.580
Thank you.

00:44:09.760 --> 00:44:13.140
It's a little and it's like just a screenshot out of an animation.

00:44:13.140 --> 00:44:15.660
And then combining dictionaries.

00:44:15.660 --> 00:44:17.420
Python 310 way is the title of the article.

00:44:17.420 --> 00:44:21.480
It really should be 3.9, but I kind of want to communicate like if you're on the latest Python,

00:44:21.480 --> 00:44:22.680
how should you be doing it?

00:44:22.680 --> 00:44:25.560
It came out in 3.9, the features that are actually in there.

00:44:25.560 --> 00:44:27.700
Anyway, the pipe stuff.

00:44:27.700 --> 00:44:32.180
Dictionary 1, pipe, dictionary 2, pipe, dictionary 3, which is all fun.

00:44:32.180 --> 00:44:35.820
And then I wanted to talk about a feature over on pypi.org.

00:44:35.820 --> 00:44:36.980
I don't even know how I found this.

00:44:37.120 --> 00:44:39.620
Probably just like an accident, like bump the keyboard or something.

00:44:39.620 --> 00:44:43.460
But if I'm over here and you just want to search for something, forward slash.

00:44:43.460 --> 00:44:44.580
Now you can search.

00:44:44.580 --> 00:44:45.180
What?

00:44:45.180 --> 00:44:47.340
So they now have a beam in the browser.

00:44:47.340 --> 00:44:49.340
Exactly.

00:44:49.340 --> 00:44:53.160
So if you're on pypi.org and you want to search, forward slash.

00:44:53.160 --> 00:44:53.940
Yes.

00:44:53.940 --> 00:44:55.540
So that's pretty cool.

00:44:55.540 --> 00:44:56.260
Yep.

00:44:56.260 --> 00:44:56.720
All right.

00:44:56.720 --> 00:44:57.920
That's it for the extras.

00:44:57.920 --> 00:44:59.000
Nice.

00:44:59.000 --> 00:45:00.780
I don't even remember what my joke is.

00:45:00.780 --> 00:45:01.520
So that's good.

00:45:01.520 --> 00:45:02.540
That'd be fine.

00:45:02.540 --> 00:45:03.840
I mean, you're new to us all.

00:45:03.840 --> 00:45:05.860
You ready?

00:45:05.860 --> 00:45:06.520
Yeah.

00:45:06.940 --> 00:45:07.340
All right.

00:45:07.340 --> 00:45:08.060
Yeah.

00:45:08.060 --> 00:45:08.620
Here we go.

00:45:08.620 --> 00:45:09.320
Oh, yeah.

00:45:09.320 --> 00:45:13.160
This is another one of these sort of like frustration type of things.

00:45:13.160 --> 00:45:13.680
That's great.

00:45:13.680 --> 00:45:17.600
This comes from the Programming Humor Twitter account.

00:45:17.600 --> 00:45:21.680
You know, twitter.com slash Programming Humor, which is there's a lot of good stuff in there.

00:45:21.680 --> 00:45:22.920
Some that I really liked.

00:45:22.920 --> 00:45:24.620
I didn't want to necessarily put on the show.

00:45:24.620 --> 00:45:28.020
But this one is developers really frustrated.

00:45:28.020 --> 00:45:30.260
Like they're sucking in on their lips.

00:45:30.260 --> 00:45:31.160
They're pulling on their cheeks.

00:45:31.160 --> 00:45:32.500
They're going, oh, I hate this job.

00:45:32.500 --> 00:45:33.560
I hate my life.

00:45:33.560 --> 00:45:35.600
Why is this happening to me?

00:45:36.020 --> 00:45:36.620
Never mind.

00:45:36.620 --> 00:45:37.660
I misspelled a variable.

00:45:37.660 --> 00:45:40.620
Good to go.

00:45:40.620 --> 00:45:41.860
Yeah.

00:45:41.860 --> 00:45:42.540
Linting.

00:45:42.540 --> 00:45:42.840
Yeah.

00:45:42.840 --> 00:45:43.660
Linting is good.

00:45:43.660 --> 00:45:44.100
Linting.

00:45:44.100 --> 00:45:44.560
Indeed.

00:45:44.560 --> 00:45:45.240
Indeed.

00:45:45.240 --> 00:45:50.960
If you just flip through the Programming Humor one, it's pretty good.

00:45:50.960 --> 00:45:54.980
You know, this eight-year-old is learning Python after dealing with the syntax bug.

00:45:54.980 --> 00:45:59.920
She asked, if the computer knows it's missing a semicolon here, why won't it add it itself?

00:46:00.420 --> 00:46:01.120
I don't know.

00:46:01.120 --> 00:46:02.020
I really don't know.

00:46:02.020 --> 00:46:02.580
Yeah.

00:46:02.580 --> 00:46:04.300
Yeah.

00:46:04.300 --> 00:46:07.780
And like, so he follows up and says what he meant.

00:46:07.780 --> 00:46:10.000
He meant colon, not semicolon.

00:46:10.000 --> 00:46:12.040
But so many people are like, semicolon?

00:46:12.040 --> 00:46:13.780
We need to use a semicolon for Python.

00:46:13.780 --> 00:46:16.460
Exactly.

00:46:16.460 --> 00:46:17.780
There are uses.

00:46:17.780 --> 00:46:18.780
They're rare though.

00:46:18.780 --> 00:46:19.300
All right.

00:46:19.300 --> 00:46:21.720
Well, fantastic.

00:46:23.020 --> 00:46:24.600
That last one.

00:46:24.600 --> 00:46:26.520
See?

00:46:26.520 --> 00:46:27.920
Yeah.

00:46:27.920 --> 00:46:29.800
It shall not be spoken, but it's good, right?

00:46:29.800 --> 00:46:30.220
Okay.

00:46:30.220 --> 00:46:31.440
There's a lot of good stuff.

00:46:31.440 --> 00:46:33.560
I recommend people go flip through that Twitter account.

00:46:33.560 --> 00:46:34.440
Nice.

00:46:34.440 --> 00:46:36.060
Brian, thank you.

00:46:36.060 --> 00:46:37.420
As always, it's good to be back with you.

00:46:37.420 --> 00:46:38.600
It's good to be back.

00:46:39.200 --> 00:46:44.160
And Dean, thanks for coming on this side of the presentation and joining us for the show.

00:46:44.160 --> 00:46:45.240
Thanks for having me.

00:46:45.240 --> 00:46:47.860
Thanks for listening to Python Bytes.

00:46:47.860 --> 00:46:50.680
Follow the show on Twitter via at Python Bytes.

00:46:50.680 --> 00:46:53.880
That's Python Bytes as in B-Y-T-E-S.

00:46:53.880 --> 00:46:57.040
Get the full show notes over at pythonbytes.fm.

00:46:57.440 --> 00:47:02.280
If you have a news item we should cover, just visit Python Bytes.fm and click submit in the

00:47:02.280 --> 00:47:02.700
nav bar.

00:47:02.700 --> 00:47:04.820
We're always on the lookout for sharing something cool.

00:47:04.820 --> 00:47:09.700
If you want to join us for the live recording, just visit the website and click live stream

00:47:09.700 --> 00:47:12.600
to get notified of when our next episode goes live.

00:47:12.600 --> 00:47:17.180
That's usually happening at noon Pacific on Wednesdays over at YouTube.

00:47:17.180 --> 00:47:21.120
On behalf of myself and Brian Okken, this is Michael Kennedy.

00:47:21.120 --> 00:47:24.840
Thank you for listening and sharing this podcast with your friends and colleagues.

