WEBVTT

00:00:00.001 --> 00:00:04.460
Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to

00:00:04.460 --> 00:00:12.780
your earbuds. This is episode 197, recorded August 26th, 2020. Brian, can you believe it's

00:00:12.780 --> 00:00:15.700
the end of August? Even if I can't say it, it still is true?

00:00:15.700 --> 00:00:18.680
No, I can't. I don't know where August went. I just don't.

00:00:18.680 --> 00:00:23.120
I thought this whole pandemic thing would make the summer seem long and slow. It seems like it

00:00:23.120 --> 00:00:23.700
just went faster.

00:00:23.700 --> 00:00:28.800
Yeah, I've got like a Lego kit that I was planning on doing like the first week of summer vacation,

00:00:28.800 --> 00:00:33.320
and it's still sitting here. So yeah, for sure. Yeah, there's a lot of things I want to get done

00:00:33.320 --> 00:00:38.420
before the sun goes away and rain starts for six months straight. That's a Pacific Northwest problem,

00:00:38.420 --> 00:00:42.660
but it's our problem. All right. Now this episode is brought to you by us as well. We'll tell you

00:00:42.660 --> 00:00:46.840
more about the things that we're doing that we think you will appreciate later. Right now,

00:00:46.840 --> 00:00:50.880
I want to talk about something that I think we might've covered before, but I don't know if we've

00:00:50.880 --> 00:00:55.140
ever satisfactorily covered it. Maybe this time we'll get a little closer and that's AsyncIO.

00:00:55.140 --> 00:00:56.580
Oh yeah, I think that's a new topic.

00:00:56.800 --> 00:01:05.080
It's a totally new topic. Covered only less than GUIs. No. So there's a new, how should I put it,

00:01:05.080 --> 00:01:14.260
a new compatibility-like layer library that allows you to work a little bit better with AsyncIO

00:01:14.260 --> 00:01:22.720
and some of the other Async libraries that are not directly immediately the same as or built right on top of AsyncIO.

00:01:23.120 --> 00:01:32.840
Curio from David Beasley and Trio from Nathaniel Smith. So there's an article that talks about this. I'm going to mention as part of this conversation.

00:01:32.840 --> 00:01:40.320
And then say, Hey, Python has three well-known concurrency libraries built around Async and await syntax. AsyncIO, Curio, and Trio.

00:01:40.320 --> 00:01:45.900
True. But where's Unsync, people? Unsync is the best of all four of those. I don't know where Unsync is.

00:01:46.040 --> 00:01:55.320
Anyway, Unsync is not part of this conversation, but Unsync plays a role a little bit like this thing I'm going to mention today is AnyIO.

00:01:56.460 --> 00:02:03.900
And it's a pretty clever name because the idea is that it provides structured concurrency primitives built on top of AsyncIO.

00:02:03.900 --> 00:02:04.320
Okay.

00:02:04.320 --> 00:02:10.700
Right? So one of the challenges with AsyncIO is you can kick off a bunch of tasks and then not wait for them.

00:02:10.700 --> 00:02:12.860
And your program can exit or you can do other things.

00:02:12.860 --> 00:02:17.900
And maybe you've seen runtime warnings like task such and such was never awaited.

00:02:17.900 --> 00:02:19.600
You're like, Hmm, I wonder what that means.

00:02:19.600 --> 00:02:24.460
Well, that probably means your program exited while it was halfway done or something like that. Right?

00:02:24.880 --> 00:02:28.440
Or your thing returned a value before it waited for it to finish. Right?

00:02:28.440 --> 00:02:36.940
And at the low level, something that's a little bit frustrating or annoying that you've got to deal with is that you've got to make sure that all the stuff you started on the Async event loop,

00:02:36.940 --> 00:02:43.220
that you wait for that event loop to finish before your program completely shuts down or completely carries on.

00:02:43.860 --> 00:02:46.620
And so that's basically the idea of this library.

00:02:46.620 --> 00:02:55.900
It's a compatibility layer across those three types, those three different well-known concurrency libraries that provides this structured concurrency.

00:02:55.900 --> 00:03:08.580
So you look at Wikipedia, they say structured concurrency is a programming paradigm aimed at improving the clarity, quality, and development time of a computer program by using a structured approach to concurrent programming.

00:03:08.780 --> 00:03:17.740
The core concept is encapsulations of threads of execution by way of control flow constructs that have a clear entry and exit points.

00:03:17.740 --> 00:03:27.780
In Python, this mostly manifests itself through this library as async with blocks or async context managers.

00:03:28.380 --> 00:03:30.000
So you're like, I'm going to do some async work.

00:03:30.000 --> 00:03:32.780
So let's create a width block, do all the work in there.

00:03:32.780 --> 00:03:41.660
And then by the way, when you leave the width block, it's going to have made sure all the tasks that were started and the tasks started by those tasks and so on all finished.

00:03:41.660 --> 00:03:42.500
Oh, that's nice.

00:03:42.500 --> 00:03:43.420
Yeah, that's pretty cool.

00:03:44.160 --> 00:03:55.380
So the way it works is you basically go anyio.createTaskGroup and then from the task group, you can spawn other subtasks and it will keep track of those.

00:03:55.380 --> 00:04:00.780
If there's an exception, I believe it will cancel the other undone ones, the unfinished ones and so on.

00:04:00.780 --> 00:04:08.980
So it's about saying we're just going to go through this thing and it's all going to run here and like it enters at the top and it exits at the bottom of the width block.

00:04:08.980 --> 00:04:09.340
Okay.

00:04:09.460 --> 00:04:10.100
That's pretty cool, right?

00:04:10.100 --> 00:04:10.320
Yeah.

00:04:10.320 --> 00:04:10.620
Yeah.

00:04:10.620 --> 00:04:12.440
So I think that that's pretty neat.

00:04:12.440 --> 00:04:13.620
Also has other primitives.

00:04:13.620 --> 00:04:15.000
So that's like a real simple example.

00:04:15.000 --> 00:04:20.080
Other example or other things it does include synchronization, primitives, locks.

00:04:20.080 --> 00:04:30.840
So if you create a reentrant lock in Python, often called a critical section and things like C++ and whatnot, it's never, ever going to help you.

00:04:30.840 --> 00:04:32.340
Well, maybe that's a little bit strong.

00:04:32.480 --> 00:04:39.500
It's likely not going to help you because those mechanisms come from the operating system process level.

00:04:39.500 --> 00:04:42.100
And what they do is they make sure two threads don't run at the same time.

00:04:42.100 --> 00:04:49.260
Well, with asyncio, it's all a bunch of stuff that's being broken apart on a single thread, right?

00:04:49.260 --> 00:04:56.140
It's all on the one, wherever the event loop.run is, run to complete or whatever, like wherever that's happening, that's the thread.

00:04:56.140 --> 00:04:57.620
So like the thread locks don't matter.

00:04:57.620 --> 00:04:58.920
It's all the same thread.

00:04:59.120 --> 00:05:00.420
Like you're not going to block anything.

00:05:00.420 --> 00:05:10.160
So having primitives that will kind of function like threads to protect data while stuff is happening, while it's in temporarily invalid states, that's pretty cool for asyncio.

00:05:10.160 --> 00:05:10.640
Okay.

00:05:10.640 --> 00:05:12.860
So you need it or you don't need it?

00:05:12.860 --> 00:05:13.660
You probably need it.

00:05:13.660 --> 00:05:19.640
I think people often don't really think too much about these invalid states that programs get into.

00:05:19.640 --> 00:05:21.540
And you think, well, asyncio, it's going to be fine.

00:05:21.540 --> 00:05:25.860
And a lot of times what you're doing with asyncio is kind of standalone.

00:05:26.240 --> 00:05:28.120
Like I'm going to kick off this thing.

00:05:28.120 --> 00:05:30.140
And when it comes back, I'm going to take the data and do something.

00:05:30.140 --> 00:05:36.680
But if you're modifying shared data structures, you could still end up in some kind of event loop, erase condition.

00:05:36.680 --> 00:05:43.640
It's not as bad as like true threading because you're not going to, it's, I don't believe it's like a plus equals, right?

00:05:43.640 --> 00:05:47.640
Of something that actually might be multiple steps at the lower level runtime.

00:05:47.640 --> 00:05:50.220
I don't think that it would get broken up to that fine grained.

00:05:50.220 --> 00:05:59.600
But if you say like debit this account, this amount of money, or await, debit this account, this amount of money, await, put that amount into the other one.

00:05:59.600 --> 00:06:05.720
And some other one is like reading in some kind of loop, like that level of higher order, like temporarily invalid state.

00:06:05.720 --> 00:06:09.840
That could be a problem for asyncio and you want some kind of lock.

00:06:09.840 --> 00:06:18.020
So this comes with that, it comes with streams, which are similar to queues, timeouts through things like move on after or fail after a certain amount of time and so on.

00:06:18.020 --> 00:06:19.260
So it's pretty cool little library.

00:06:19.260 --> 00:06:20.100
Yeah, that's nice.

00:06:20.100 --> 00:06:20.460
Nice.

00:06:20.460 --> 00:06:24.440
My vote still for unsync is the best of the four, even though it was unmentioned.

00:06:24.440 --> 00:06:27.960
Isn't unsync built on those also?

00:06:28.120 --> 00:06:35.440
It's a compatibility layer that takes asyncio, threading, and multiprocessing and turns them all into things that you can await.

00:06:35.440 --> 00:06:36.080
Oh, yeah.

00:06:36.080 --> 00:06:36.820
Yeah.

00:06:36.820 --> 00:06:42.720
So don't you think there should be like a standard, like a, they should get together like some consortium and have a standard about this?

00:06:42.720 --> 00:06:43.300
Yeah.

00:06:43.300 --> 00:06:48.760
Well, they probably should, but we're still in the early stages of figuring out what the right API is.

00:06:48.760 --> 00:06:50.240
That's right.

00:06:50.240 --> 00:06:51.040
That's why they haven't done it.

00:06:51.040 --> 00:06:57.380
There's something else that has, that could use some standards and that's in a lot of data science libraries.

00:06:58.040 --> 00:07:03.140
There's an announcement that there's a new consortium for Python data API standards.

00:07:03.140 --> 00:07:06.800
So there is one happening and it's happening actually quite fast.

00:07:06.800 --> 00:07:13.000
They're getting started right away and there's activities to the announcements right away.

00:07:13.000 --> 00:07:23.000
Then in September, I believe they're going to kick off some work on data frames or on, no, starting with arrays and then move on to data frames.

00:07:23.000 --> 00:07:25.440
And so, okay, I'm getting ahead of myself.

00:07:25.940 --> 00:07:37.920
There are little blurbs says, one of the unintended consequences of the advances in multiple frameworks for data science, machine learning, deep learning, and numerical computing is that there is fragmentation.

00:07:37.920 --> 00:07:43.320
And in using the tools and then there are differences in common function signatures.

00:07:43.680 --> 00:07:58.200
They have one example that shows what the, generally a mean function to get the average or mean, people are going to like flame me for calling average mean, but as a commoner, I kind of think of those the same thing.

00:07:58.200 --> 00:08:04.120
But anyway, they show eight different, frameworks then, and some of them are common with other ones.

00:08:04.120 --> 00:08:09.920
And so there's five different interfaces for over the eight frameworks for just the mean function for an array.

00:08:09.920 --> 00:08:10.140
Yeah.

00:08:10.140 --> 00:08:12.880
And what's crazy is like, they all are basically the same.

00:08:12.880 --> 00:08:18.340
They're so, so similar, but they're not the same, not code wise the same, but they might as well be.

00:08:18.340 --> 00:08:18.780
Yeah.

00:08:18.780 --> 00:08:26.920
And so one of the issues is there's people are using more than one framework for different parts of their, maybe different parts of their data flow.

00:08:27.620 --> 00:08:37.020
And sometimes you can kind of forget which one you're using and having a lot of these things common actually would just make life easier, I think.

00:08:37.020 --> 00:08:49.420
So I think, I don't know how far they'll get with this, but I think it's a really, so they're not trying to make all of these, these frameworks look exactly the same, but with, commonalities in arrays and data frames.

00:08:49.900 --> 00:08:53.940
Or, and they note that arrays are also called tensors.

00:08:53.940 --> 00:09:01.720
So those are, trying to make some of those common is, I think a really good idea for some of the easy, simple stuff.

00:09:01.720 --> 00:09:02.560
why not?

00:09:02.560 --> 00:09:03.700
It seems like a great idea.

00:09:03.700 --> 00:09:05.180
It seems like a huge challenge though.

00:09:05.180 --> 00:09:12.500
Like who's going to give, whose function is going to be the one that's like, yeah, we're dropping this part of our API to make it look like everyone else's.

00:09:12.500 --> 00:09:12.900
Right.

00:09:12.900 --> 00:09:19.700
And that's why I think that they've, they've went through a lot of thought on how to go about with this process and try to convince people.

00:09:19.820 --> 00:09:43.380
So they're working with, they're trying to kind of be in between the framework authors and maintainers and the community and try to do some, some review process for different APIs, put a proposal out, have feedback from both from, from the different projects and from the community to have, have more of a, you know, more input to try to make it.

00:09:43.380 --> 00:09:47.480
It isn't just like one set of people saying, Hey, I think this should be this way.

00:09:47.480 --> 00:09:48.940
Yeah, no, it's, it's a good idea.

00:09:49.060 --> 00:09:53.460
It would be great if a lot of these applications or these frameworks may be renamed.

00:09:53.460 --> 00:10:01.180
If it's the same function, if it's like, for instance, mean in this example, if it's spelled exactly the same, maybe it should be the same API.

00:10:01.180 --> 00:10:08.480
And if you want a special version of it, maybe have a, have a underscore with an extra, you know, some reason why it's different.

00:10:08.480 --> 00:10:10.640
you can have extra different functions.

00:10:10.640 --> 00:10:11.060
Yeah.

00:10:11.060 --> 00:10:13.820
It seems like you could find some pretty good common ground here.

00:10:13.920 --> 00:10:14.520
It's a good idea.

00:10:14.520 --> 00:10:21.440
And if they make it happen, you know, it'd just be easier to mix and match frameworks and use the best or different situations.

00:10:21.440 --> 00:10:24.720
Cause I can certainly see you're like, Oh, I'm working with pandas here.

00:10:24.720 --> 00:10:31.460
It would be great if I could do this on CUDA cores with QPy, but I don't really know that it's close, but it's not the same.

00:10:31.460 --> 00:10:35.520
So I'm just going to keep stroking along here as opposed to change the import statement.

00:10:35.520 --> 00:10:36.260
Now it runs there.

00:10:36.260 --> 00:10:36.560
Yep.

00:10:36.820 --> 00:10:43.160
I don't know if it's ever really going to be like, you can just swap out a different framework, but for some of the common stuff, it'd really be great.

00:10:43.160 --> 00:10:50.060
And that's why one of the reasons why we're bringing it up is so that people can get on board and start being part of this review process if they care about it.

00:10:50.060 --> 00:10:50.260
Yeah.

00:10:50.260 --> 00:11:04.480
It also seems like there might be some room for like adaptive layers, like from QPy import pandas layer or something like that, where it basically, you talk to the, in terms of say a pandas API and it converts it to its internal.

00:11:04.660 --> 00:11:09.460
It's like, Oh, these, these arguments are switched in order or this keyword is named differently or whatever.

00:11:09.460 --> 00:11:11.080
And there's even things like differences.

00:11:11.080 --> 00:11:22.760
And even if the API looks the same or it's very similar, the default might be like in some cases, the default might be none versus false or versus no value or things.

00:11:22.760 --> 00:11:25.140
I don't know what no value means, but anyway.

00:11:25.140 --> 00:11:26.940
Yep.

00:11:26.940 --> 00:11:27.440
Cool.

00:11:27.440 --> 00:11:28.680
That's a good one.

00:11:28.680 --> 00:11:31.940
Now, also good is the things that we're working on.

00:11:31.940 --> 00:11:34.000
Brian, you want to tell folks about our Patreon?

00:11:34.220 --> 00:11:48.340
Actually, we've kind of silently announced it a while ago, but we've got 47 patrons now and it's set up for a monthly contribution and we, everything really appreciate people helping out because there are some expenses with the show.

00:11:48.340 --> 00:11:50.260
So that's a really cool.

00:11:50.260 --> 00:11:51.280
We'd love to see that grow.

00:11:51.720 --> 00:11:57.460
I don't, we'd also like to hear from people about how we'd like to come up with some special thank you benefits for patrons.

00:11:57.460 --> 00:12:00.760
And so I'd like to have ideas come from the community.

00:12:00.760 --> 00:12:04.120
If you can come up with some ideas, we will think about it.

00:12:04.120 --> 00:12:04.700
Yeah.

00:12:04.740 --> 00:12:06.520
So, and I'm trying to figure out how to get to it.

00:12:06.520 --> 00:12:08.440
So on our Python bytes.

00:12:08.440 --> 00:12:11.380
If you're on any episode page, it's there on the right.

00:12:11.380 --> 00:12:11.740
Okay.

00:12:11.740 --> 00:12:13.040
If you go to an episode page.

00:12:13.040 --> 00:12:13.560
Got it.

00:12:13.560 --> 00:12:13.760
Yep.

00:12:13.760 --> 00:12:19.460
Then it says on the right, I believe somewhere it says sponsors on, off the double check.

00:12:19.460 --> 00:12:20.140
I believe it does.

00:12:20.140 --> 00:12:20.540
Okay.

00:12:20.540 --> 00:12:21.440
We'll double check.

00:12:21.580 --> 00:12:23.060
It can for sure.

00:12:23.060 --> 00:12:24.400
If it doesn't already.

00:12:24.400 --> 00:12:29.260
And also, I want to just tell folks about a couple of things going on over at Talk Python

00:12:29.260 --> 00:12:30.000
training.

00:12:30.000 --> 00:12:35.160
We're doing a webcast on helping people move from using Excel for all their data analysis

00:12:35.160 --> 00:12:41.040
to pandas, basically moving from Excel to the Python data science stack, which has all sorts

00:12:41.040 --> 00:12:43.200
of cool benefits and really neat things you can do there.

00:12:43.200 --> 00:12:48.060
So Chris Moffitt is going to come on and writing a course with us and he's going to do a webcast,

00:12:48.060 --> 00:12:51.160
which I announced it like, well, yeah.

00:12:51.160 --> 00:12:54.580
It's 15 hours ago and already has like 600 people signed up for it.

00:12:54.580 --> 00:12:55.540
So it's free.

00:12:55.540 --> 00:12:56.620
People can just come sign up.

00:12:56.620 --> 00:12:59.500
It happens late September, September 29th.

00:12:59.500 --> 00:13:03.160
I'll put the link at the extra section of the show notes so people can find it there.

00:13:03.160 --> 00:13:07.080
And also the Python memory management course is out for early access.

00:13:07.080 --> 00:13:08.760
A bunch of people are signing up and enjoying it.

00:13:08.760 --> 00:13:13.740
So if you want to get to it soon, get to it early, people can check that out as well.

00:13:13.740 --> 00:13:14.500
Very exciting.

00:13:14.500 --> 00:13:17.260
So this next one I want to talk about has to do with manners.

00:13:17.260 --> 00:13:19.500
What kind of developer are you?

00:13:19.500 --> 00:13:20.720
Are you a polite developer?

00:13:21.040 --> 00:13:22.060
You're talking to the framework.

00:13:22.060 --> 00:13:26.780
Are you always checking in with it to see how it feels, what you're allowed to do?

00:13:26.780 --> 00:13:27.980
Are you kind of a rebel?

00:13:27.980 --> 00:13:29.360
You're just going to do what you like.

00:13:29.360 --> 00:13:33.620
But every now and then you get smacked down by the framework with an exception.

00:13:33.620 --> 00:13:38.900
I don't want to describe how a developer I am because I don't want the explicit tag on this episode.

00:13:40.360 --> 00:13:46.100
So there's an article that talks about something I think is pretty fun and interesting to consider.

00:13:46.100 --> 00:13:55.200
And it talks about the two types of error handling patterns or mechanisms that you might use when you're writing code.

00:13:55.840 --> 00:13:57.860
And Python naturally leans towards one.

00:13:57.860 --> 00:14:00.200
But there might be times you don't want to use it.

00:14:00.700 --> 00:14:05.860
And that is it's the two patterns are it's easier to ask for forgiveness than permission.

00:14:05.860 --> 00:14:07.360
That's one.

00:14:07.520 --> 00:14:10.520
And the other one is look before you leap or please may I.

00:14:10.520 --> 00:14:11.180
All right.

00:14:12.080 --> 00:14:18.840
And with the look before you leap, it's a lot of checks, like something you might do in C code.

00:14:18.840 --> 00:14:21.620
So you would say, I'm going to create a file.

00:14:21.620 --> 00:14:23.940
Oh, does the folder exist?

00:14:23.940 --> 00:14:27.100
If the folder doesn't exist, I'm going to need to create the folder.

00:14:27.100 --> 00:14:29.500
And then I can put the file there.

00:14:29.500 --> 00:14:30.780
Do I have permission to write the file?

00:14:30.780 --> 00:14:31.100
Yes.

00:14:31.100 --> 00:14:31.340
Okay.

00:14:31.340 --> 00:14:32.600
Then I'll go ahead and write the file.

00:14:32.600 --> 00:14:33.140
Right.

00:14:33.140 --> 00:14:37.040
You're always checking if I can do this, if this is in the right state and so on.

00:14:37.440 --> 00:14:39.380
That's the look before you leap style.

00:14:39.380 --> 00:14:45.940
The ask for forgiveness style is just try with open this thing.

00:14:45.940 --> 00:14:46.860
Oh, that didn't work.

00:14:46.860 --> 00:14:48.040
Catch exception.

00:14:48.040 --> 00:14:48.740
Right.

00:14:48.740 --> 00:14:51.260
Except some IO error or something like that.

00:14:51.260 --> 00:14:54.160
So there's reasons you might want to use both.

00:14:54.160 --> 00:14:58.760
Python leans or nudges you towards the ask for forgiveness.

00:14:58.760 --> 00:15:00.420
Try except version.

00:15:00.420 --> 00:15:04.380
The reason is, let's say you're opening a file and it's a JSON file.

00:15:04.380 --> 00:15:05.800
You might check first.

00:15:05.800 --> 00:15:07.280
Does the file exist?

00:15:07.520 --> 00:15:07.740
Yes.

00:15:07.740 --> 00:15:08.700
Do I have permission to read it?

00:15:08.700 --> 00:15:09.060
Yes.

00:15:09.060 --> 00:15:09.420
Okay.

00:15:09.420 --> 00:15:10.300
Open the file.

00:15:10.300 --> 00:15:11.420
Well, guess what?

00:15:11.420 --> 00:15:16.880
What if the file's malformed and you try to feed it over to like JSON load and you give

00:15:16.880 --> 00:15:17.540
it the file pointer?

00:15:17.540 --> 00:15:20.180
It's not going to say, sorry, it's malformed.

00:15:20.180 --> 00:15:21.320
It's going to raise an exception.

00:15:21.320 --> 00:15:25.600
It's not going to return it like a value, like malformed, constant, weird thing.

00:15:25.600 --> 00:15:29.500
It's just going to throw an exception and say, you know, invalid thing on line seven or

00:15:29.500 --> 00:15:29.720
whatever.

00:15:29.720 --> 00:15:30.000
Right.

00:15:30.000 --> 00:15:35.740
And so what that means is even if you wanted to do the look before you leap, you probably can't

00:15:35.740 --> 00:15:39.120
test everything and you're going to end up in a situation where you're still going to

00:15:39.120 --> 00:15:41.320
have to have the try except block anyway.

00:15:41.320 --> 00:15:44.360
So maybe you should just always do that.

00:15:44.360 --> 00:15:45.100
Right.

00:15:45.100 --> 00:15:47.680
Maybe you should just go, well, we're going to have to have exception handling anyway.

00:15:48.240 --> 00:15:52.700
That's just, we're going to do exception handling as much as possible and not do these tests.

00:15:52.700 --> 00:15:55.640
So that's the, this article over here.

00:15:55.640 --> 00:15:58.480
It's on the switwoski.com.

00:15:58.480 --> 00:16:00.540
Oh yeah.

00:16:00.540 --> 00:16:01.340
It's on Sebastian.

00:16:01.340 --> 00:16:02.780
Widooski.

00:16:02.780 --> 00:16:06.740
So yeah, it's his, I didn't realize that it was his article.

00:16:06.740 --> 00:16:08.900
So it's, his article.

00:16:09.060 --> 00:16:15.960
Anyway, he talks about like, what is the relative performance of these things and tries to talk

00:16:15.960 --> 00:16:18.120
about it from a, well, sure.

00:16:18.120 --> 00:16:22.400
It's cool to think of how it looks in code, but is one faster or one slower than the other?

00:16:22.400 --> 00:16:22.820
Okay.

00:16:22.820 --> 00:16:25.040
And this actually came up on talk Python as well.

00:16:25.040 --> 00:16:30.400
And so I said, look, if we're going to come up with an example, let's have a class and a

00:16:30.400 --> 00:16:31.000
base class.

00:16:31.340 --> 00:16:34.200
And let's have the base class define an attribute.

00:16:34.200 --> 00:16:36.680
And sometimes let's try to access the attribute.

00:16:36.680 --> 00:16:41.000
And when you don't have the base class, it'll, or when you only have the base class, it'll

00:16:41.000 --> 00:16:41.580
crash, right?

00:16:41.580 --> 00:16:42.640
Cause it's in the derived class.

00:16:42.640 --> 00:16:45.080
So let's say we have two ways to test.

00:16:45.080 --> 00:16:49.260
We could either ask, does it have the attribute and then try to access it?

00:16:49.260 --> 00:16:52.020
Or we could just right to access it.

00:16:52.020 --> 00:16:56.720
And it says, well, look, if it, if it works all the time and you're not actually getting

00:16:56.720 --> 00:17:00.820
errors and you're doing this, it's 30% slower to do the look before you leap.

00:17:00.820 --> 00:17:05.920
Cause you're doing an extra test and basically the try accept block is more or less free.

00:17:05.920 --> 00:17:11.900
Like it doesn't cost anything if there's not actually an error, but if you turn it around

00:17:11.900 --> 00:17:14.380
and you say, no, it's not there.

00:17:14.380 --> 00:17:20.300
All of a sudden it turns out the ask the try accept block is four times slower.

00:17:20.300 --> 00:17:21.700
That's a lot slower.

00:17:21.700 --> 00:17:22.340
Oh really?

00:17:22.340 --> 00:17:27.980
Because the raising of the exception, figuring out this call stack, all that kind of stuff

00:17:27.980 --> 00:17:28.540
is expensive.

00:17:28.540 --> 00:17:30.480
So instead of just going, does it have the attribute?

00:17:30.480 --> 00:17:34.360
You're going, well, let's do the whole call stack thing, every error, right?

00:17:34.360 --> 00:17:37.100
And create an object and throw it and all that kind of stuff.

00:17:37.100 --> 00:17:39.240
So it's a lot slower when there are errors.

00:17:39.240 --> 00:17:46.120
And anyway, it's a, an interesting thing to consider if you care about performance and things

00:17:46.120 --> 00:17:51.300
like parsing integers or parsing data that might sometimes fail, might not, you know, sometimes

00:17:51.300 --> 00:17:51.940
it doesn't fail.

00:17:51.940 --> 00:17:52.380
Yeah.

00:17:52.380 --> 00:17:52.780
Okay.

00:17:52.780 --> 00:17:54.120
Devil's advocate here.

00:17:54.120 --> 00:17:59.560
His example doesn't have any activity in the ask for forgiveness.

00:17:59.560 --> 00:18:01.080
If it isn't there.

00:18:01.080 --> 00:18:02.960
That's the way I saw when I first read it as well.

00:18:02.960 --> 00:18:04.100
There's two sections.

00:18:04.100 --> 00:18:08.460
There's like one part where he says, let's do it with the attribute on the drive class

00:18:08.460 --> 00:18:13.220
and let's do it again a second time by taking away the attribute and seeing what it's like.

00:18:13.220 --> 00:18:13.560
Right.

00:18:13.560 --> 00:18:17.340
But I mean, the code that if it, if it isn't exist, it just doesn't do anything.

00:18:17.340 --> 00:18:17.720
Right.

00:18:17.720 --> 00:18:20.280
Whereas in reality, you're still going to have to do something.

00:18:20.280 --> 00:18:20.500
Yeah.

00:18:20.500 --> 00:18:21.060
You got to do something.

00:18:21.060 --> 00:18:21.360
Notify the user.

00:18:21.360 --> 00:18:22.100
It's wrong.

00:18:22.100 --> 00:18:22.360
Yeah.

00:18:22.360 --> 00:18:22.700
Whatever.

00:18:22.700 --> 00:18:23.120
Yeah.

00:18:23.120 --> 00:18:23.280
Okay.

00:18:23.280 --> 00:18:23.520
Yeah.

00:18:23.520 --> 00:18:23.840
For sure.

00:18:23.840 --> 00:18:24.380
That's a good point.

00:18:24.380 --> 00:18:26.320
Like it's just basically a try except pass.

00:18:26.320 --> 00:18:26.840
Yeah.

00:18:27.240 --> 00:18:28.260
So what do you think about this?

00:18:28.260 --> 00:18:35.400
So what I think is you're going to have to write the try except anyway, almost all the

00:18:35.400 --> 00:18:35.700
time.

00:18:35.700 --> 00:18:38.520
And you don't want both.

00:18:38.520 --> 00:18:39.980
Like that doesn't seem good.

00:18:39.980 --> 00:18:42.340
That seems like just extra complexity.

00:18:42.340 --> 00:18:46.540
So when it makes sense, just go with ask for forgiveness.

00:18:46.540 --> 00:18:47.820
Just embrace exceptions.

00:18:47.820 --> 00:18:48.640
Right.

00:18:48.640 --> 00:18:52.320
Remember you have a finally block that often can like get rid of a test as well.

00:18:52.760 --> 00:18:57.660
You have multiple types of error except clauses are based on error type.

00:18:57.660 --> 00:19:00.100
I think people should do a lot with that.

00:19:00.100 --> 00:19:04.580
That said, if your goal is to like parse specific data, right?

00:19:04.580 --> 00:19:09.800
Like I'm going to read this number I got off by off of the internet by web scraping and there's

00:19:09.800 --> 00:19:10.700
a million records here.

00:19:10.700 --> 00:19:11.460
I'm going to parse it.

00:19:11.460 --> 00:19:15.280
If you want to do that a lot, a lot faster, that might make a lot of sense.

00:19:15.280 --> 00:19:20.900
I actually have a gist example that I put up trying to compare the speed of these things

00:19:20.900 --> 00:19:22.240
in a mixed case.

00:19:22.240 --> 00:19:26.680
So like the cases we're looking at here are kind of strange because it's like, well,

00:19:26.680 --> 00:19:29.300
there's, it's all errors or it's zero errors.

00:19:29.300 --> 00:19:29.620
Right.

00:19:29.620 --> 00:19:31.920
And then it doesn't really do anything, which are both weird.

00:19:31.920 --> 00:19:35.460
So I have this one where it comes up with like a million records strings.

00:19:35.460 --> 00:19:40.900
And most of the time they're number, they're legitimate numbers, like 4.2 as a string.

00:19:40.900 --> 00:19:41.760
And then you can parse it.

00:19:42.220 --> 00:19:48.400
And what I found was if you have more than 4% errors, I think it was four, like 4.5% or

00:19:48.400 --> 00:19:53.700
something errors, Aaron's data, it's slower to use exceptions.

00:19:53.700 --> 00:19:54.160
Okay.

00:19:54.160 --> 00:19:55.380
The cutoff is 4% errors.

00:19:55.440 --> 00:19:58.800
And I think if you have more than 4% errors, then the exceptions become more expensive.

00:19:58.800 --> 00:19:59.260
That's right.

00:19:59.260 --> 00:20:02.940
Anyway, it's something that people can run and get real numbers out of and play with it in

00:20:02.940 --> 00:20:04.640
a slightly more concrete way.

00:20:04.640 --> 00:20:05.720
But I don't know.

00:20:05.720 --> 00:20:06.460
What do you think?

00:20:06.500 --> 00:20:12.020
I think you start out by focusing on the code and making it easy and clear to understand

00:20:12.020 --> 00:20:13.100
and then worry about this stuff.

00:20:13.100 --> 00:20:13.600
Yeah.

00:20:13.600 --> 00:20:15.340
So I don't actually put either.

00:20:15.340 --> 00:20:18.660
I don't usually do the asking or the checking stuff.

00:20:18.660 --> 00:20:23.500
And that is one of the things that's good about bringing this up is that is more common in

00:20:23.500 --> 00:20:28.660
Python code is to not check stuff, just to, you know, to just go ahead and do it.

00:20:28.660 --> 00:20:30.620
And then I write a lot of tests.

00:20:30.620 --> 00:20:32.600
So I write a lot of tests around things.

00:20:32.600 --> 00:20:33.020
Yeah.

00:20:33.160 --> 00:20:39.240
And so either case checking for things or like, for instance, if it is, if it is input,

00:20:39.240 --> 00:20:41.200
if I've got user input, I'm checking for things.

00:20:41.200 --> 00:20:41.400
Yeah.

00:20:41.400 --> 00:20:45.580
I'm going to do it checks ahead of time because I want, because the behavior of what happens

00:20:45.580 --> 00:20:50.060
when it isn't there or when there's a problem, it isn't really a problem.

00:20:50.060 --> 00:20:55.360
It needs to be designed into the system as to what behavior to do when something unexpected

00:20:55.360 --> 00:20:55.900
happens.

00:20:55.900 --> 00:21:00.520
But the, in normal code, like, well, what happens if there's not an attribute?

00:21:00.520 --> 00:21:03.140
Well, you shouldn't be in that situation, right?

00:21:03.140 --> 00:21:04.300
You shouldn't be in that situation.

00:21:04.300 --> 00:21:05.580
And I usually push it up higher.

00:21:05.580 --> 00:21:08.060
I don't have try accept blocks all over the place.

00:21:08.060 --> 00:21:16.260
I have them around APIs that might not be trustworthy or around external systems or something.

00:21:16.260 --> 00:21:19.960
I don't put try accept blocks around code that I'm calling on my own code.

00:21:19.960 --> 00:21:20.980
Things like that.

00:21:20.980 --> 00:21:21.460
Yeah.

00:21:21.460 --> 00:21:22.520
I'm with you on that.

00:21:22.520 --> 00:21:23.300
That makes a lot of sense.

00:21:23.800 --> 00:21:27.760
The one time that I'll do the test, the look before you leave style, is if I think I can

00:21:27.760 --> 00:21:28.640
fix it, right?

00:21:28.640 --> 00:21:30.100
Does this directory not exist?

00:21:30.100 --> 00:21:31.360
I'm going to write a file to it.

00:21:31.360 --> 00:21:32.600
Well, I'm just going to make the directory.

00:21:32.600 --> 00:21:35.380
Then I'm going to write to it, you know?

00:21:35.380 --> 00:21:37.760
Those kinds of tests can get you out of trouble.

00:21:37.760 --> 00:21:41.940
But if you're just going to say this didn't work, chances are, you know, you still need

00:21:41.940 --> 00:21:44.080
the error handling and exception format anyway.

00:21:44.080 --> 00:21:44.480
Yeah.

00:21:44.480 --> 00:21:45.740
And you're probably going to throw an exception.

00:21:45.740 --> 00:21:46.920
So, anyway.

00:21:46.920 --> 00:21:47.800
Cool.

00:21:47.800 --> 00:21:52.760
So, you probably should get your code right, test it, and then just stick it in GitHub.

00:21:52.760 --> 00:21:54.480
Get in your repository.

00:21:54.480 --> 00:21:56.000
Make sure it's all up to date, right?

00:21:56.000 --> 00:21:58.520
Oh, I was wondering how you were going to do that transition.

00:21:58.520 --> 00:21:59.140
So, yeah.

00:21:59.140 --> 00:21:59.640
That's good.

00:21:59.640 --> 00:22:05.420
I was following a discussion on Twitter, and I think, actually, I think Anthony Shaw may

00:22:05.420 --> 00:22:06.660
have started it, but I can't remember.

00:22:06.660 --> 00:22:11.860
But dealing with different, if you've got a lot of repositories, just sometimes you have

00:22:11.860 --> 00:22:16.440
a lot of maintenance to do or a little, you know, some common things you're doing for

00:22:16.440 --> 00:22:17.320
a whole bunch of repos.

00:22:17.920 --> 00:22:24.640
And there's lots of different reasons why that might be the case or related tools or

00:22:24.640 --> 00:22:25.940
maybe just your work.

00:22:25.940 --> 00:22:26.960
You've got a lot of repos.

00:22:26.960 --> 00:22:31.300
But there's a project that came up in this discussion that I hadn't really played with

00:22:31.300 --> 00:22:33.320
before, and it's a project called My Repos.

00:22:33.320 --> 00:22:38.780
And on the site, it says you've got a lot of version control repositories.

00:22:38.780 --> 00:22:43.280
Sometimes you want to update them all at once or push out all your local changes.

00:22:43.880 --> 00:22:48.480
You may use special command lines in some repos to implement specific workflows.

00:22:48.480 --> 00:22:54.180
Well, the My Repos project provides an MR command, which is a tool to manage all your version

00:22:54.180 --> 00:22:55.260
control repositories.

00:22:56.200 --> 00:22:59.120
And the way it works is it's on directory structures.

00:22:59.120 --> 00:23:05.600
So it's a, and I usually have all of my repos that I'm working with under a common, like,

00:23:05.600 --> 00:23:08.400
projects directory or something so that I know where to look.

00:23:08.800 --> 00:23:12.120
And so I'm already set up for something like this might work.

00:23:12.120 --> 00:23:17.360
And you go into, into one of your repos and you type, if you have this installed, you type

00:23:17.360 --> 00:23:18.280
MR register.

00:23:18.280 --> 00:23:24.600
And it registers this under, registers that repo for common commands.

00:23:24.780 --> 00:23:30.940
And then whether you're in a parent directory or one of the specific directories and type

00:23:30.940 --> 00:23:36.960
a command, like for instance, if you say MR status, it'll do status on all of the repos

00:23:36.960 --> 00:23:40.820
that you care about or update or diff or something like that.

00:23:40.820 --> 00:23:47.340
And then you can build up even more complex commands yourself to do more complicated things.

00:23:47.520 --> 00:23:51.040
But I would, I mean, I'm probably going to use it right away just for just checking the

00:23:51.040 --> 00:23:56.000
status or doing polls or updates or something like that on, on lots of repos.

00:23:56.000 --> 00:23:57.120
So this looks neat.

00:23:57.120 --> 00:23:57.700
Yeah, it looks neat.

00:23:57.700 --> 00:23:58.920
I like the idea a lot.

00:23:58.920 --> 00:24:01.960
So basically I'm the same as you.

00:24:01.960 --> 00:24:08.180
I've got a directory, maybe a couple of levels, but all of my GitHub repos go in there, right?

00:24:08.180 --> 00:24:10.360
I grouped them by like personal stuff or work stuff.

00:24:10.360 --> 00:24:13.600
But other than that, they're just all next to each other.

00:24:13.600 --> 00:24:17.140
And this would just let you say, go do a Git poll on all of them.

00:24:17.280 --> 00:24:17.680
That's great.

00:24:17.680 --> 00:24:17.960
Yeah.

00:24:17.960 --> 00:24:22.860
Or like, for instance, at work, I've got often like three or four different related repos

00:24:22.860 --> 00:24:27.020
that if I switch to another project that I'm working on, I need to go through and make sure

00:24:27.020 --> 00:24:31.740
I'm not sure what branch I'm using or if everything's up to date.

00:24:31.740 --> 00:24:37.640
So being able to just go through all, like even two or three, being able to go and update

00:24:37.640 --> 00:24:41.640
them all at once or just even check the status of all, it'll save time.

00:24:41.640 --> 00:24:47.140
And then for the show, at least somebody that interviewed for a testing code, at least,

00:24:47.140 --> 00:24:53.300
Adam Johnson wrote an article called maintaining multiple Python projects with my repos.

00:24:53.300 --> 00:24:55.780
And we'll link to his article in the show notes.

00:24:55.780 --> 00:24:56.080
Yeah.

00:24:56.080 --> 00:24:56.440
Perfect.

00:24:56.440 --> 00:24:59.380
I like this idea enough that I wrote something like that already.

00:24:59.380 --> 00:24:59.860
You did.

00:24:59.860 --> 00:25:05.660
Well, what I wrote is something that will, it'll go and actually synchronize my GitHub account

00:25:05.660 --> 00:25:09.380
with a folder structure on my computer.

00:25:09.760 --> 00:25:15.120
So I'll go and just say like repo sync or whatever I called it.

00:25:15.240 --> 00:25:21.580
And it'll use the GitHub API to go and figure out all the repos that I've cloned or created

00:25:21.580 --> 00:25:26.060
and the different organizations like talk Python organization versus my personal one.

00:25:26.060 --> 00:25:29.620
And then it'll create folders based on the organization or where I forked it from and then

00:25:29.620 --> 00:25:30.100
clone it.

00:25:30.100 --> 00:25:32.300
And if it's already there, it'll update it within.

00:25:32.300 --> 00:25:34.040
It'll like basically pull all this down.

00:25:34.280 --> 00:25:34.960
Oh, that's cool.

00:25:34.960 --> 00:25:35.900
I need that.

00:25:35.900 --> 00:25:37.200
It was a lot of work.

00:25:37.200 --> 00:25:39.920
This seems like it's pre-built and pretty close.

00:25:39.920 --> 00:25:41.120
So it looks pretty nice.

00:25:41.120 --> 00:25:44.640
The one thing it doesn't do is it doesn't look like it doesn't go to GitHub and say, oh,

00:25:44.640 --> 00:25:47.220
what other repos have you created that you maybe don't have here?

00:25:47.220 --> 00:25:48.600
Maybe you want that.

00:25:48.600 --> 00:25:49.040
Maybe you don't.

00:25:49.040 --> 00:25:53.700
If you've like forked Windows source code and it's like 50 gigs, you don't want this tool

00:25:53.700 --> 00:25:54.940
that I'm talking about.

00:25:54.940 --> 00:25:58.080
But if you have reasonable size things like I forked Linux.

00:25:58.080 --> 00:25:58.840
Okay, great.

00:25:58.840 --> 00:25:59.820
That's going to take a while.

00:25:59.820 --> 00:26:03.080
But normally it would be I think it would be pretty neat.

00:26:03.080 --> 00:26:03.380
Yeah.

00:26:03.380 --> 00:26:06.220
Another thing that's neat around managing these types of things is Docker.

00:26:06.220 --> 00:26:10.340
And did you know that Python has an official Docker image?

00:26:10.340 --> 00:26:11.040
I did not.

00:26:11.040 --> 00:26:11.580
I didn't either.

00:26:11.580 --> 00:26:17.140
Well, I recently heard that, but it's fairly new news to me that there is an official Docker

00:26:17.140 --> 00:26:18.280
Python image.

00:26:18.280 --> 00:26:24.900
So theoretically, if you want to work with some kind of Linux Docker machine that uses Python,

00:26:24.900 --> 00:26:29.560
you can go and Docker run or to create the Python one.

00:26:29.560 --> 00:26:29.980
Right.

00:26:29.980 --> 00:26:32.940
So it's not super surprising.

00:26:32.940 --> 00:26:34.760
It's just called Python.

00:26:34.760 --> 00:26:35.280
Right.

00:26:35.280 --> 00:26:37.360
But it's yeah, it's just called Python.

00:26:37.360 --> 00:26:37.780
That's it.

00:26:37.780 --> 00:26:38.360
I believe so.

00:26:38.360 --> 00:26:40.900
Pretty straightforward working with it.

00:26:40.900 --> 00:26:47.900
But I'm going to talk about like basically looking through that Docker, that official Docker

00:26:47.900 --> 00:26:48.480
image.

00:26:48.480 --> 00:26:53.540
So Itamar Turner Trouring, who was on Talk Python not long ago, talking about Phil.

00:26:53.720 --> 00:26:57.700
And we also talked about Phil and Python bytes, the data science focused memory tool.

00:26:57.700 --> 00:27:02.680
He wrote an article called a deep dive into the official Docker image for Python.

00:27:02.680 --> 00:27:09.320
So basically it's like, well, if there's an official Docker image for Python, what is it?

00:27:09.320 --> 00:27:10.100
How do you set it up?

00:27:10.100 --> 00:27:15.200
Because understanding how it's set up is basically how do you take a machine that has no Python

00:27:15.200 --> 00:27:18.180
whatsoever and configure it in a Python way?

00:27:18.380 --> 00:27:20.420
So this is using Debian.

00:27:20.420 --> 00:27:22.740
That's just what it's based on.

00:27:22.740 --> 00:27:28.780
And it's using the Buster version because apparently Debian names all their releases after characters

00:27:28.780 --> 00:27:29.600
from Toy Story.

00:27:29.600 --> 00:27:30.720
I didn't know that.

00:27:30.720 --> 00:27:31.340
But yep.

00:27:31.340 --> 00:27:31.860
Buster.

00:27:31.860 --> 00:27:34.160
Buster is the current one.

00:27:34.420 --> 00:27:36.560
So it's going to create a Docker image.

00:27:36.560 --> 00:27:37.840
You create the Docker file.

00:27:37.840 --> 00:27:42.300
You say this Docker image is based on some other foundational one.

00:27:42.300 --> 00:27:43.740
So Debian Buster.

00:27:43.740 --> 00:27:50.940
And then it sets up slash user slash local slash bin for the environmental path.

00:27:50.940 --> 00:27:52.440
Because that is the first thing in the path.

00:27:52.440 --> 00:27:54.460
Because that's where it's going to put Python.

00:27:54.920 --> 00:28:01.040
It sets the locale explicitly to the EMV language is to UTF-8.

00:28:01.040 --> 00:28:05.920
There's some debate about whether this is actually necessary because current Python also defaults

00:28:05.920 --> 00:28:06.560
UTF-8.

00:28:06.560 --> 00:28:08.060
But, you know, here it is.

00:28:08.060 --> 00:28:13.760
And then it also sets an environment variable Python underscore version to whatever the Python

00:28:13.760 --> 00:28:14.380
version is.

00:28:14.380 --> 00:28:15.620
Right now it's 385.

00:28:15.620 --> 00:28:17.480
But whatever it is, that's kind of cool.

00:28:17.480 --> 00:28:22.040
So you can ask, hey, what version is in this system without actually touching Python?

00:28:22.500 --> 00:28:23.140
That's cool.

00:28:23.140 --> 00:28:27.840
And then it has to do a few things like register the CA certificates.

00:28:27.840 --> 00:28:32.480
Like, I've had people sending me messages that are taking courses.

00:28:32.480 --> 00:28:36.660
And they're trying to run the code from, you know, something that talks to requests,

00:28:36.660 --> 00:28:41.640
whether it's SSL certificate endpoint, HTTPS endpoint.

00:28:41.640 --> 00:28:45.760
And they'll say, this thing says the certificate is invalid.

00:28:45.760 --> 00:28:47.400
I'm like, the certificate's not invalid.

00:28:47.400 --> 00:28:48.160
What's going on here?

00:28:48.160 --> 00:28:48.680
Right?

00:28:48.680 --> 00:28:53.320
And almost always, something about the way that Python got set up on their machine didn't

00:28:53.320 --> 00:28:55.560
run the create certificate command.

00:28:55.560 --> 00:29:01.120
So there's like this step where Python will go download all the major certificate authorities

00:29:01.120 --> 00:29:02.640
and like trust them in the system.

00:29:02.640 --> 00:29:03.600
So that happens next.

00:29:03.600 --> 00:29:07.980
And then it actually will set up things like GCC and whatnot.

00:29:07.980 --> 00:29:09.780
So it can compile it.

00:29:09.780 --> 00:29:10.960
It's interesting.

00:29:10.960 --> 00:29:13.620
Downloads the source code, compiles it.

00:29:13.700 --> 00:29:17.300
But then what's interesting is it uninstalls the compiler tools.

00:29:17.300 --> 00:29:21.160
It's like, okay, we're going to download Python and we're going to compile it.

00:29:21.160 --> 00:29:23.700
But you didn't explicitly ask for GCC.

00:29:23.700 --> 00:29:24.760
We just needed it.

00:29:24.760 --> 00:29:25.600
So those are gone.

00:29:25.600 --> 00:29:26.160
Right?

00:29:26.160 --> 00:29:29.000
Cleans up the PYC files and all those kinds of things.

00:29:29.000 --> 00:29:33.840
And then it gives an alias to say that Python 3 is the same as Python.

00:29:34.040 --> 00:29:36.400
Like the command, you could do it without the 3.

00:29:36.400 --> 00:29:41.500
Another thing that we've gone on about that's annoying is like, I created a virtual environment.

00:29:41.500 --> 00:29:43.180
Oh, it has the wrong version of pip.

00:29:43.180 --> 00:29:44.280
Is my pip out of date?

00:29:44.280 --> 00:29:45.640
Your pip's probably out of date.

00:29:45.640 --> 00:29:46.880
Everyone's pip is out of date.

00:29:46.880 --> 00:29:52.540
Unless you're like a rare, like two-week window where Python has been released at the same time

00:29:52.540 --> 00:29:54.420
like the modern pip has been released.

00:29:54.420 --> 00:29:56.040
So guess what?

00:29:56.140 --> 00:29:59.120
They upgrade pip to the new version, which is cool.

00:29:59.120 --> 00:30:04.120
And then finally it sets the entry point of the Docker container,

00:30:04.120 --> 00:30:08.740
which is the default command to do if you just say Docker run this image,

00:30:08.740 --> 00:30:12.560
like Docker run Python 3.8-slim-buster.

00:30:12.560 --> 00:30:16.420
If you just say that by itself, what program is going to run?

00:30:16.420 --> 00:30:19.860
Because the way it works is it basically starts Linux and then runs one program.

00:30:19.860 --> 00:30:22.460
And that program exits, the Docker container goes away.

00:30:22.460 --> 00:30:25.400
And so it sets that to be the Python 3 command.

00:30:25.400 --> 00:30:32.780
So basically, if you Docker run the Python Docker image, you're going to get just the REPL.

00:30:32.780 --> 00:30:33.680
Interesting.

00:30:33.680 --> 00:30:37.340
Yeah, you can always run it with different endpoints like Bash and then go in and like do stuff to it

00:30:37.340 --> 00:30:40.320
or run it with MicroWSGI or Nginx or whatever.

00:30:40.320 --> 00:30:44.040
But if you don't, you're just going to get Python 3 REPL.

00:30:44.040 --> 00:30:52.300
Anyway, that's the way the official Python Docker image configures itself from a bare Debian buster

00:30:52.300 --> 00:30:53.900
over to Python 3.

00:30:53.900 --> 00:30:54.260
Neat.

00:30:54.260 --> 00:30:54.720
Yeah, neat.

00:30:54.720 --> 00:30:57.320
I thought it might be worth just thinking about like, what are all the steps?

00:30:57.320 --> 00:31:00.000
And you know, how does that happen on your computer?

00:31:00.000 --> 00:31:01.860
No, that's good.

00:31:01.860 --> 00:31:04.720
Because yeah, I have been curious about that.

00:31:04.720 --> 00:31:07.300
I was going to throw Python on a Docker image.

00:31:07.300 --> 00:31:08.740
What does that get me?

00:31:08.740 --> 00:31:09.660
Yeah, exactly.

00:31:09.660 --> 00:31:10.100
And that's what it is.

00:31:10.100 --> 00:31:14.980
Oh, you could also apt install Python 3 dash dev.

00:31:18.100 --> 00:31:19.060
Yeah, that might be cheating.

00:31:19.060 --> 00:31:19.400
All right.

00:31:19.400 --> 00:31:20.420
What's this final one?

00:31:20.420 --> 00:31:27.100
Oh, so it was recommended by, we covered some craziness that Anthony did an episode or two

00:31:27.100 --> 00:31:27.400
ago.

00:31:27.400 --> 00:31:32.060
And somebody commented that maybe we need to only in a pandemic section.

00:31:32.820 --> 00:31:33.820
Yeah, that sounds fun.

00:31:33.820 --> 00:31:36.280
So I selected Nanner Most.

00:31:36.280 --> 00:31:37.980
No, sorry.

00:31:37.980 --> 00:31:38.760
Nanner Nest.

00:31:38.760 --> 00:31:42.180
It's an optimal peanut butter and banana sandwich placement.

00:31:42.980 --> 00:31:46.740
So this is kind of an awesome article by Ethan Rosenthal.

00:31:46.740 --> 00:31:51.960
Talks about during the pandemic, he's been sort of having trouble doing anything.

00:31:52.760 --> 00:31:58.320
And so he really liked peanut butter and banana sandwiches when he was just still even.

00:31:58.320 --> 00:32:01.240
He got picked this habit up from his grandfather, I think.

00:32:01.240 --> 00:32:07.760
Anyway, this is using Python and computer vision and deep learning and machine learning and a

00:32:07.760 --> 00:32:13.260
whole bunch of cool libraries to come up with the best packing algorithm for a particular

00:32:13.260 --> 00:32:16.020
banana and the particular bread that you have.

00:32:16.020 --> 00:32:22.180
So you take a picture that includes both the bread and the bananas or the banana you have.

00:32:22.720 --> 00:32:28.480
And it will come up with the optimal slicing and placement of the banana for your banana

00:32:28.480 --> 00:32:28.880
sandwich.

00:32:28.880 --> 00:32:33.340
Wow, this is like a banana maximization optimization problem.

00:32:33.340 --> 00:32:36.380
So if you want, you got to see the pictures to get this.

00:32:36.380 --> 00:32:42.300
So like, if you're going to cut your banana into slices, and obviously the radius of the

00:32:42.300 --> 00:32:45.120
banana slice varies at where you cut it in the banana, right?

00:32:45.120 --> 00:32:45.920
Is it near the top?

00:32:45.920 --> 00:32:46.420
Is it in the middle?

00:32:46.420 --> 00:32:48.600
It's going to result in different size slices.

00:32:48.760 --> 00:32:56.100
On where do you place your bread, the banana circles on your bread to have maximum surface

00:32:56.100 --> 00:32:59.020
area of bananas relative to the what's left of the bread, right?

00:32:59.020 --> 00:32:59.720
Something like that?

00:32:59.720 --> 00:33:00.100
Yes.

00:33:00.100 --> 00:33:05.180
And he's trying to maximize, make it so that you have almost all of the bites of the sandwich

00:33:05.180 --> 00:33:08.260
have an equal ratio of banana, peanut butter, and bread.

00:33:08.380 --> 00:33:09.160
Oh yeah, okay.

00:33:09.160 --> 00:33:10.500
It's all about the flavor.

00:33:10.500 --> 00:33:16.720
I didn't understand like the real motivation, but yeah, you want to have an equal layer, right?

00:33:16.720 --> 00:33:18.860
So you don't want that spot where you just get bread.

00:33:19.080 --> 00:33:23.880
You actually learn quite a bit about all these different processes and there's quite a bit

00:33:23.880 --> 00:33:31.320
of math here talking about coming up with arcs for, you have to estimate the banana shape as

00:33:31.320 --> 00:33:40.260
part of an ellipse and using the radius of that to determine banana slices and estimates for,

00:33:40.260 --> 00:33:45.820
because you're looking at a banana sideways, you have to estimate what the shape of the banana

00:33:45.820 --> 00:33:49.460
circle will be and it's not really a circle, it's more of an ellipse also.

00:33:49.460 --> 00:33:51.240
Yeah, there's a lot going on here.

00:33:51.240 --> 00:33:56.300
Some advanced stuff to deliver your bananas perfectly.

00:33:56.300 --> 00:33:57.960
I love it.

00:33:57.960 --> 00:33:58.980
Actually, this is really interesting.

00:33:58.980 --> 00:33:59.480
This is cool.

00:33:59.480 --> 00:34:03.640
And it's, I mean, it's a silly application, but it's also a neat example.

00:34:03.640 --> 00:34:09.500
Yeah, actually, and this would be, I think, a cool thing for, to talk about difficult problems

00:34:09.500 --> 00:34:14.320
and packing for like a teaching, like in a school setting.

00:34:14.440 --> 00:34:18.580
I think this would be a great example to talk about some of these different complex problems.

00:34:18.580 --> 00:34:19.480
Yeah, totally.

00:34:19.480 --> 00:34:21.940
Well, that's it for our main items.

00:34:21.940 --> 00:34:26.960
For the extras, I just wanted to say I'll put the links for the Excel to Python webcast and

00:34:26.960 --> 00:34:30.180
the memory measurement course down there and we'll put the Patreon link as well.

00:34:30.180 --> 00:34:32.020
Let's see if you have anything else you want to share.

00:34:32.020 --> 00:34:32.720
No, that's good.

00:34:32.720 --> 00:34:33.120
Yeah, cool.

00:34:33.120 --> 00:34:34.720
How about sharing a joke?

00:34:34.720 --> 00:34:35.780
A joke would be great.

00:34:35.780 --> 00:34:41.660
So I'm going to describe the situation and you can be the interviewer slash boss who has the

00:34:41.660 --> 00:34:42.360
caption, okay?

00:34:42.360 --> 00:34:42.780
Okay.

00:34:42.780 --> 00:34:45.160
So the first, there's two scenarios.

00:34:45.160 --> 00:34:47.040
The title is Job Requirements.

00:34:47.040 --> 00:34:49.040
This comes to us from Eduardo Orochena.

00:34:49.040 --> 00:34:50.340
Thanks for that.

00:34:50.340 --> 00:34:56.740
And the first scenario is the job interview where you're getting hired.

00:34:56.900 --> 00:35:02.040
And then there's the reality, which is later, which is the actual on the job day to day.

00:35:02.040 --> 00:35:06.820
So on the job interview, I come in, I'm an applicant here and Brian, the boss says.

00:35:06.820 --> 00:35:09.260
Invert a binary tree on this whiteboard.

00:35:09.260 --> 00:35:15.000
Or some other random data structure, like quick sort this, but using some other weird thing,

00:35:15.000 --> 00:35:15.180
right?

00:35:15.180 --> 00:35:15.400
Yeah.

00:35:15.440 --> 00:35:20.760
Something that is kind of really computer science-y, way out there, probably not going

00:35:20.760 --> 00:35:22.940
to do, but kind of maybe makes sense, right?

00:35:22.940 --> 00:35:23.260
All right.

00:35:23.260 --> 00:35:25.920
Now I'm at the job and I've got like my computer.

00:35:25.920 --> 00:35:29.100
I have a huge purple buy button on my website that I'm working on.

00:35:29.100 --> 00:35:31.280
And the boss says, make the button bigger.

00:35:31.280 --> 00:35:33.000
Yep.

00:35:33.000 --> 00:35:33.900
That's the job.

00:35:33.900 --> 00:35:37.020
Yeah.

00:35:37.020 --> 00:35:38.580
Very nice.

00:35:38.580 --> 00:35:39.740
Good, good.

00:35:39.740 --> 00:35:40.020
All right.

00:35:40.020 --> 00:35:42.460
Well, I love the jokes and all the tech we're covering.

00:35:42.460 --> 00:35:42.960
Thanks, Brian.

00:35:42.960 --> 00:35:43.360
Yeah.

00:35:43.360 --> 00:35:43.760
Thank you.

00:35:43.760 --> 00:35:44.000
Yeah.

00:35:44.000 --> 00:35:44.200
Bye.

00:35:44.200 --> 00:35:46.180
Thank you for listening to Python Bytes.

00:35:46.180 --> 00:35:48.700
Follow the show on Twitter via at Python Bytes.

00:35:48.700 --> 00:35:51.540
That's Python Bytes as in B-Y-T-E-S.

00:35:51.540 --> 00:35:54.780
And get the full show notes at pythonbytes.fm.

00:35:54.780 --> 00:35:59.000
If you have a news item you want featured, just visit pythonbytes.fm and send it our way.

00:35:59.000 --> 00:36:01.700
We're always on the lookout for sharing something cool.

00:36:01.700 --> 00:36:04.800
On behalf of myself and Brian Okken, this is Michael Kennedy.

00:36:04.800 --> 00:36:08.240
Thank you for listening and sharing this podcast with your friends and colleagues.

