WEBVTT

00:00:00.001 --> 00:00:02.820
A recent Twitter poll went around the web, and it asked,

00:00:02.820 --> 00:00:06.360
what percentage of the Python standard library do you think you know?

00:00:06.360 --> 00:00:11.680
Someone copied me on it, maybe expecting some really high percentage answer like 80-90%.

00:00:11.680 --> 00:00:16.260
In reality, what I did answer, and my rough estimate still is, that's probably around 50%.

00:00:16.260 --> 00:00:21.540
This episode with Alessandro Molina definitely helped confirm that estimate for me.

00:00:21.540 --> 00:00:25.920
He just published a book entitled Modern Python Standard Library Cookbook,

00:00:26.140 --> 00:00:30.660
and it's full of these great little corners of the standard library that you might not have bumped into,

00:00:30.660 --> 00:00:33.140
but you'll be super glad to hear about them on this episode.

00:00:33.140 --> 00:00:38.540
It's Talk Python To Me, episode 197, recorded January 10th, 2019.

00:00:38.540 --> 00:00:55.100
Welcome to Talk Python To Me, a weekly podcast on Python,

00:00:55.100 --> 00:00:58.100
the language, the libraries, the ecosystem, and the personalities.

00:00:58.100 --> 00:01:00.000
This is your host, Michael Kennedy.

00:01:00.000 --> 00:01:02.140
Follow me on Twitter, where I'm @mkennedy.

00:01:02.140 --> 00:01:05.880
Keep up with the show and listen to past episodes at talkpython.fm,

00:01:05.880 --> 00:01:08.320
and follow the show on Twitter via at Talk Python.

00:01:08.320 --> 00:01:12.020
This episode is sponsored by Linode and Rollbar.

00:01:12.020 --> 00:01:14.220
Please check out what they're offering during their segments.

00:01:14.220 --> 00:01:15.760
It really helps support the show.

00:01:15.760 --> 00:01:18.620
Alessandro, welcome back to Talk Python.

00:01:18.620 --> 00:01:20.120
Hi, Michael. Thank you.

00:01:20.120 --> 00:01:22.340
Yeah, it's great to have you on the show again.

00:01:23.060 --> 00:01:28.540
It's been a long time since back on episode 35 when we talked about Turbogears,

00:01:28.540 --> 00:01:32.600
and you're back with a new project that I think is really, really cool,

00:01:32.600 --> 00:01:37.480
a look at the standard library through a modern Python lens,

00:01:37.480 --> 00:01:39.680
which I'm excited to dig into with you.

00:01:40.520 --> 00:01:44.360
But first, maybe just tell us, what have you been up to the last couple of years

00:01:44.360 --> 00:01:45.360
since we heard from you on the show?

00:01:45.360 --> 00:01:51.240
Yeah, actually, I've been mostly doing Python development as usual.

00:01:51.240 --> 00:01:57.800
That's my most prominent interest in open source development.

00:01:58.340 --> 00:02:01.820
I started a bunch of open source projects aside of Turbogears,

00:02:01.820 --> 00:02:05.100
but I'm still moving Turbogears forward,

00:02:05.100 --> 00:02:08.160
and we actually just released version 2.4,

00:02:08.160 --> 00:02:11.340
which had a major rewrite of the framework,

00:02:11.340 --> 00:02:14.120
which is great because we are trying to keep it modern.

00:02:14.120 --> 00:02:17.320
The standard library is doing a great job at that.

00:02:17.320 --> 00:02:18.740
Yeah, that's really cool.

00:02:18.740 --> 00:02:21.480
What are some of the other projects that you've been working on,

00:02:21.480 --> 00:02:23.480
the other open source ones you've released?

00:02:23.880 --> 00:02:27.660
One of the most interesting projects, I think, was DuckPy,

00:02:27.660 --> 00:02:32.780
which is, I will say, JavaScript execution environment for Python,

00:02:32.780 --> 00:02:35.700
because one of the problems that I faced

00:02:35.700 --> 00:02:40.660
was integrating the assets pipeline in web projects,

00:02:40.660 --> 00:02:45.540
and that always requires you to have Node.js involved

00:02:45.540 --> 00:02:48.560
and Webpack or tools like that.

00:02:48.560 --> 00:02:50.620
And I really wanted to have a solution

00:02:50.620 --> 00:02:53.660
that worked in the Python environment.

00:02:53.660 --> 00:02:56.760
Like you did pip install,

00:02:56.760 --> 00:02:59.620
and you got not only all the Python dependencies,

00:02:59.620 --> 00:03:01.860
but also the JavaScript dependencies

00:03:01.860 --> 00:03:06.120
and your ECMAScript compiled and things like that.

00:03:06.120 --> 00:03:07.080
That's pretty cool.

00:03:07.080 --> 00:03:10.540
So it's kind of like what people are using npm for

00:03:10.540 --> 00:03:13.320
on Python web apps to manage Bootstrap

00:03:13.320 --> 00:03:16.380
and AngularJS and stuff along those lines?

00:03:16.380 --> 00:03:19.460
Yeah, it's a suite of various tools, I would say,

00:03:19.540 --> 00:03:21.680
because it provides a JavaScript interpreter,

00:03:21.680 --> 00:03:26.360
which is used to run things like the TypeScript compiler

00:03:26.360 --> 00:03:29.180
or the Babel compiler.

00:03:29.180 --> 00:03:34.480
So to translate your JavaScript from more recent versions

00:03:34.480 --> 00:03:36.060
to the oldest ones,

00:03:36.060 --> 00:03:38.840
you don't need to install Node.js anymore.

00:03:38.840 --> 00:03:42.160
You can use DuckPy and have Python do that for you.

00:03:42.740 --> 00:03:46.680
And of course, it provides a command

00:03:46.680 --> 00:03:49.700
which is compatible with npm.

00:03:49.700 --> 00:03:53.400
So you can install npm packages from Python

00:03:53.400 --> 00:03:56.440
without having the need to have npm or Node.js.

00:03:56.440 --> 00:04:00.000
It's not super easy to explain

00:04:00.000 --> 00:04:03.420
because I think that people frequently get confused

00:04:03.420 --> 00:04:05.740
about its purpose when I try to explain it.

00:04:05.740 --> 00:04:08.200
But the one idea is that you can have,

00:04:08.200 --> 00:04:11.200
like, run just run your pip install

00:04:11.200 --> 00:04:15.540
and you can have your whole application compiled,

00:04:15.540 --> 00:04:19.740
JavaScript files compiled back to plain old JavaScript

00:04:19.740 --> 00:04:22.280
and all your dependencies installed,

00:04:22.280 --> 00:04:24.200
even if they are from JavaScript

00:04:24.200 --> 00:04:26.860
instead of being from Python and things like that.

00:04:26.860 --> 00:04:28.560
So it makes your life a lot easier

00:04:28.560 --> 00:04:30.680
because you don't have to care

00:04:30.680 --> 00:04:33.680
about maintaining two different packaging environments

00:04:33.680 --> 00:04:36.560
and more to different interpreters and things like that.

00:04:36.560 --> 00:04:37.280
I really like it.

00:04:37.280 --> 00:04:38.060
That sounds awesome.

00:04:38.060 --> 00:04:38.680
Cool.

00:04:38.680 --> 00:04:41.720
So let's maybe talk about this idea

00:04:41.720 --> 00:04:43.600
of the modern standard library.

00:04:43.600 --> 00:04:47.180
So, you know, there's a whole range of people listening

00:04:47.180 --> 00:04:49.520
who have different levels of experience with Python.

00:04:49.520 --> 00:04:52.340
Let's just start with what is the standard library?

00:04:52.340 --> 00:04:55.280
Standard library is practically everything

00:04:55.280 --> 00:04:57.360
that ships with Python itself.

00:04:57.360 --> 00:05:00.500
I mean, it's known that Python ships,

00:05:00.500 --> 00:05:01.680
but it is included.

00:05:02.180 --> 00:05:04.360
And the standard library have those batteries.

00:05:04.360 --> 00:05:06.820
Yeah, exactly.

00:05:06.820 --> 00:05:11.100
They're the batteries that come in the box

00:05:11.100 --> 00:05:12.060
when you get Python.

00:05:12.060 --> 00:05:14.220
You can get other batteries,

00:05:14.220 --> 00:05:15.800
but these are the built-in batteries

00:05:15.800 --> 00:05:17.200
or the ones that come included, right?

00:05:17.200 --> 00:05:19.940
Yeah, I would say that they have far more than batteries

00:05:19.940 --> 00:05:23.920
because there is tons of things that it can do for you.

00:05:23.920 --> 00:05:26.080
There is a lot inside the standard library.

00:05:26.420 --> 00:05:32.420
And I think it's also something that is not super frequently covered around

00:05:32.420 --> 00:05:34.380
in blog posts or things like that.

00:05:34.380 --> 00:05:39.720
We always tend to have, like, blog posts on the most recent new cool project

00:05:39.720 --> 00:05:43.220
or things like that that is outside of the standard library, of course,

00:05:43.220 --> 00:05:47.580
because things get included in the standard library after years they are around.

00:05:48.140 --> 00:05:53.100
But that's actually good because it means it's something you can really rely on.

00:05:53.100 --> 00:05:53.540
Absolutely.

00:05:53.540 --> 00:05:55.620
It's important that that has to stay there.

00:05:55.620 --> 00:05:59.320
And there's actually a huge bar for bringing things into the standard library.

00:05:59.320 --> 00:06:01.140
I recall a year or two ago,

00:06:01.140 --> 00:06:05.980
there was a debate about whether requests should be brought into the standard library

00:06:05.980 --> 00:06:11.020
to more or less supersede the built-in HTTP client capabilities.

00:06:11.400 --> 00:06:15.300
And they decided no, not because they felt requests wasn't good enough,

00:06:15.300 --> 00:06:21.280
but because requests was changing more quickly than the standard library could really facilitate, right?

00:06:21.280 --> 00:06:25.140
Because it's released with new features really every 18 months and things like that.

00:06:25.140 --> 00:06:26.080
Yeah, absolutely.

00:06:26.080 --> 00:06:29.380
That's one of the problems of the standard library,

00:06:29.380 --> 00:06:31.800
but also one of the reasons why it's very good.

00:06:31.800 --> 00:06:36.720
Because the things you have there, you can rely on them for the years to come.

00:06:37.060 --> 00:06:43.280
It was especially clear in the recent, in the last years,

00:06:43.280 --> 00:06:45.260
where Python changed very quick.

00:06:45.260 --> 00:06:49.200
We released many versions of Python in a very quick time.

00:06:49.200 --> 00:06:54.400
And it was clear that even if you are on Python 3.2,

00:06:54.400 --> 00:06:57.720
and you upgrade to Python 3.7,

00:06:57.720 --> 00:07:02.380
99% of what you use from the standard library is still there,

00:07:02.380 --> 00:07:05.480
works exactly like before, and you can be fine.

00:07:05.480 --> 00:07:09.200
Of course, between Python 2 and Python 3, there were major changes,

00:07:09.200 --> 00:07:11.660
but that was expected due to the B.

00:07:11.660 --> 00:07:12.800
That was intentional, right?

00:07:12.800 --> 00:07:16.540
That was like, okay, we finally have to just bite the bullet and make these changes.

00:07:16.540 --> 00:07:18.260
But other than that, you're right, it's really stable.

00:07:18.260 --> 00:07:23.260
There was some blog post or something about somebody wrote how they hated Python,

00:07:23.260 --> 00:07:25.720
and one of their reasons they claimed was,

00:07:25.720 --> 00:07:30.680
well, if you have 3.5 and you upgrade to 3.6, it might not work.

00:07:30.800 --> 00:07:34.600
I'm like, no, that's actually exactly how it works.

00:07:34.600 --> 00:07:38.480
And I'm really impressed with the stability of Python as it changes.

00:07:38.480 --> 00:07:39.600
I haven't seen any problems.

00:07:39.600 --> 00:07:44.100
The only problems I've encountered is I've used features too new on my dev machine,

00:07:44.100 --> 00:07:47.240
and then I pushed it to production where I didn't realize,

00:07:47.360 --> 00:07:50.920
oh, yeah, that feature is not yet on my server.

00:07:50.920 --> 00:07:52.480
So that's my own fault.

00:07:52.480 --> 00:07:52.860
Yeah.

00:07:52.860 --> 00:07:56.640
That happened to me from time to time too, of course.

00:07:56.640 --> 00:07:57.240
Yeah.

00:07:57.240 --> 00:08:00.040
Not often, but unfortunate when it does.

00:08:00.820 --> 00:08:06.880
So I think actually when people talk about how amazing Python is,

00:08:06.880 --> 00:08:11.040
or when they judge any programming language and they compare it against another programming language,

00:08:11.040 --> 00:08:13.240
they might compare the syntax.

00:08:13.240 --> 00:08:19.680
They might say, well, look how much easier like a foreign loop is in this language versus that language,

00:08:19.680 --> 00:08:24.220
or this exception handling block is cleaner than that exception handling block.

00:08:24.560 --> 00:08:31.000
But while that matters, I think actually what most people have in mind when they think about how they feel about a language

00:08:31.000 --> 00:08:35.960
is the standard library of the two languages and maybe the broader ecosystem as well.

00:08:35.960 --> 00:08:38.280
When I think of like, why is Python awesome?

00:08:38.280 --> 00:08:42.780
I don't think, well, because the way the language works with numbers is great.

00:08:42.780 --> 00:08:47.020
I think, well, I can import all these things and solve all these problems right away.

00:08:47.080 --> 00:08:49.120
And that's really the standard library, not the language, right?

00:08:49.120 --> 00:08:51.660
There are many things that are built in Python.

00:08:51.660 --> 00:08:56.980
They're like whenever you use the dictionary or list or things like that.

00:08:56.980 --> 00:08:59.520
They are built into the language itself.

00:08:59.520 --> 00:09:05.020
But every time you do an import, it means you are going to the standard library.

00:09:05.020 --> 00:09:08.220
Unless it's a package you installed explicitly, of course.

00:09:08.220 --> 00:09:09.100
Of course.

00:09:09.440 --> 00:09:10.920
So let's talk about your book.

00:09:10.920 --> 00:09:18.100
And your book is called the Modern Standard Library, Python Modern Standard Library Cookbook, right?

00:09:18.100 --> 00:09:23.320
And I like these cookbook ideas because you're like, well, I'm trying to solve some networking problem.

00:09:23.320 --> 00:09:27.480
Oh, here's two little recipes that I can use to solve that problem.

00:09:27.480 --> 00:09:33.880
So let's just start with what you mean by modern in the standard library cookbook here.

00:09:34.080 --> 00:09:39.980
And then we've selected a handful of specific recipes that we'll talk about that are pretty fun.

00:09:39.980 --> 00:09:50.860
Yeah, that's the modern part was the hardest part for me to find out to best decline in the implementation of the book, I would say.

00:09:50.860 --> 00:09:55.120
Because it's, of course, modern because it covers Python 3.

00:09:55.120 --> 00:09:59.540
And most of the things that are there are Python 3 specific.

00:10:00.160 --> 00:10:04.960
But I didn't want to go for, like, the only latest Python version.

00:10:04.960 --> 00:10:09.900
At the time where I started writing the book, 3.6 was just released.

00:10:09.900 --> 00:10:12.360
So we are now at 3.7.

00:10:12.360 --> 00:10:16.280
And it's probably going to happen pretty soon, 3.8.

00:10:16.280 --> 00:10:30.580
So I didn't want to follow the most recent thing because I know that in the real world, on your job, you are probably not going to be allowed to upgrade Python every single time when your release happens.

00:10:30.580 --> 00:10:42.860
So you probably are going to need the recipes that you can apply on your daily life on version of Python, which are modern, but not the most recent one.

00:10:43.220 --> 00:10:48.700
So I will say that I tried to think to things that work on Python 3.5.

00:10:48.700 --> 00:11:00.960
And I think that that's actually clear on some recipes because there are different ways to do those things in, like, more recent Python 3.6 or 3.7.

00:11:00.960 --> 00:11:11.240
But I still went for the way that you can do that in the works with Python 3.5 and the subsequent version.

00:11:11.240 --> 00:11:17.660
So I tried to balance between being modern and covering as many users as possible.

00:11:17.660 --> 00:11:18.960
You know, that's always...

00:11:19.640 --> 00:11:21.060
Yeah, it's a tough balance to strike.

00:11:21.060 --> 00:11:24.820
But I do think the choice of targeting Python 3.5 is pretty good.

00:11:24.820 --> 00:11:30.620
Like, really, if you're using Python 3, most people are on 3.5 or higher at this point.

00:11:30.620 --> 00:11:33.620
And so, I mean, you do give up a few cool things.

00:11:33.620 --> 00:11:39.800
You give up data classes and you give up f-strings and a couple of other things that would be really nice to mix in here.

00:11:39.800 --> 00:11:43.540
But at the same time, it's a little more timeless, a little more broad this way.

00:11:44.320 --> 00:11:47.680
All right, well, let's just talk about some of the recipes you have in here.

00:11:47.680 --> 00:11:50.020
So we pulled out some of the more interesting ones.

00:11:50.020 --> 00:11:55.980
You know, what I liked about going through these in your book was a lot of times I'm like,

00:11:55.980 --> 00:12:00.660
oh, I didn't know that class or that function existed and did this.

00:12:00.660 --> 00:12:05.980
And whenever I'm surprised like that, I'm like, oh, you can send emails out of the logging framework?

00:12:05.980 --> 00:12:07.680
That's pretty awesome.

00:12:07.680 --> 00:12:08.680
I didn't know that, right?

00:12:08.680 --> 00:12:11.920
So I think these will be pretty interesting to folks.

00:12:12.020 --> 00:12:14.960
And let's just start with that, like reporting errors in production.

00:12:14.960 --> 00:12:18.480
So I guess we should maybe frame this a little bit.

00:12:18.480 --> 00:12:22.520
There's lots of ways to report errors in production or do the other things we're going to talk about

00:12:22.520 --> 00:12:25.080
if you depend on some external library, right?

00:12:25.080 --> 00:12:31.760
But the goal of your book is how much awesome stuff can you do without installing or depending on other libraries,

00:12:31.760 --> 00:12:33.440
unless you absolutely have to, right?

00:12:33.440 --> 00:12:34.300
Yeah, absolutely.

00:12:34.520 --> 00:12:37.020
I think that you say that the right thing.

00:12:37.020 --> 00:12:47.720
One of the reasons why I wanted to start this book is exactly because there are a lot of things that people don't know that are available in the standard library,

00:12:47.720 --> 00:12:54.420
or they don't know that they can easily be changed to work in a different way or things like that.

00:12:54.420 --> 00:12:57.400
So like, for example, you mentioned the logging module.

00:12:57.400 --> 00:13:01.080
And we probably all know that it exists.

00:13:01.080 --> 00:13:07.420
And many of us are using it on the daily world to log messages on things like that.

00:13:07.420 --> 00:13:12.340
But the interesting thing is that it has many different handlers.

00:13:12.460 --> 00:13:16.840
So you can send the output of what you are logging in many different places.

00:13:16.840 --> 00:13:23.940
And one of those handlers actually sends the output to an SMTP server, so by mail.

00:13:23.940 --> 00:13:28.000
So you could send your logging messages by mail.

00:13:28.000 --> 00:13:28.200
Right.

00:13:28.200 --> 00:13:32.300
So you've got a logging.handlers.smtphandler class, right?

00:13:32.300 --> 00:13:32.520
Yeah.

00:13:32.520 --> 00:13:33.460
You just plug that in.

00:13:33.460 --> 00:13:34.480
Yeah, it's built in.

00:13:34.480 --> 00:13:34.880
Exactly.

00:13:35.620 --> 00:13:44.820
And, of course, sending all your logged messages as in an email doesn't make too much sense because it will make your life very hard.

00:13:44.820 --> 00:13:48.620
But for some specific messages, it might make sense.

00:13:48.620 --> 00:13:57.580
And as you can configure the logging method to filter only some messages, like in this case, we are talking about logging errors, so logging exceptions.

00:13:57.580 --> 00:14:02.240
You can use the logger to only report exception by mail.

00:14:02.240 --> 00:14:07.020
So every time an exception happens in your code, you get notified and you are aware.

00:14:07.020 --> 00:14:11.780
You don't have to wait for the user to come to you and complain that the software is not working.

00:14:11.780 --> 00:14:13.520
You can answer, I already know.

00:14:13.520 --> 00:14:14.660
I've already fixed it.

00:14:14.660 --> 00:14:15.740
Yeah, and that's really cool.

00:14:15.740 --> 00:14:31.880
And in your example, you do this a lot, actually, and I like this, is you create decorators that you can use to, say, decorate a method and say if there's an error here, you know, exception here, email it to us and things like that, right?

00:14:31.880 --> 00:14:32.560
Yes, exactly.

00:14:32.560 --> 00:14:46.040
In the recipe, it explains how to achieve that with decorator, which usually you will throw the decorator at the main function of your program so that every exception that happens is reported to your user.

00:14:46.040 --> 00:14:58.940
But if you are, for example, writing a web application, you might want to decorate the whiskey main callable because the main application is actually the application server, not your own code.

00:14:58.940 --> 00:15:13.180
But the core idea is that if you apply that decorator to the beginning of your code base, then everything that crashes within your code base will be reported to you and you'll know when the program fails.

00:15:15.620 --> 00:15:18.620
This portion of Talk Python To Me is brought to you by Linode.

00:15:18.620 --> 00:15:22.340
Are you looking for hosting that's fast, simple, and incredibly affordable?

00:15:22.340 --> 00:15:27.460
Well, look past that bookstore and check out Linode at talkpython.fm/Linode.

00:15:27.460 --> 00:15:29.360
That's L-I-N-O-D-E.

00:15:29.360 --> 00:15:33.780
Plans start at just $5 a month for a dedicated server with a gig of RAM.

00:15:34.100 --> 00:15:39.800
They have 10 data centers across the globe, so no matter where you are or where your users are, there's a data center for you.

00:15:39.800 --> 00:15:54.220
Whether you want to run a Python web app, host a private Git server, or just a file server, you'll get native SSDs on all the machines, a newly upgraded 200 gigabit network, 24-7 friendly support, even on holidays, and a seven-day money-back guarantee.

00:15:54.220 --> 00:15:55.860
Need a little help with your infrastructure?

00:15:55.860 --> 00:16:00.580
They even offer professional services to help you with architecture, migrations, and more.

00:16:00.580 --> 00:16:03.540
Do you want a dedicated server for free for the next four months?

00:16:03.540 --> 00:16:06.600
Just visit talkpython.fm/Linode.

00:16:06.600 --> 00:16:11.800
It looks like you could probably take this idea and extend it.

00:16:11.800 --> 00:16:23.600
Like, if you wrote a web service of some sort that you could call and report your errors, maybe log it in your own database, you could create your own custom handler, stick it into the login framework, and do the same thing.

00:16:23.600 --> 00:16:28.680
Instead of sending mail, it logs it remotely to your service, which goes in your database for reporting and whatnot, right?

00:16:28.680 --> 00:16:29.480
Yeah, absolutely.

00:16:29.740 --> 00:16:36.040
You can actually write your own custom handler and log the messages and the exceptions wherever you prefer.

00:16:36.040 --> 00:16:51.920
But I think that, as usual, most of the modules available in the standard library are very good when you have an average, medium level of complexity of your needs.

00:16:51.920 --> 00:17:11.980
Because when you're using the standard library, it's a great way to report your errors.

00:17:11.980 --> 00:17:32.500
So, you might want to switch to a national external service that is dedicated to that.

00:17:32.660 --> 00:17:40.260
Sure, but maybe you have some level of data protection or you don't want to share your tracebacks and all that kind of stuff, right?

00:17:40.260 --> 00:17:45.100
So, you might want to keep it custom for privacy or IP reasons.

00:17:45.100 --> 00:17:45.920
All right.

00:17:45.920 --> 00:17:46.840
So, that's really cool.

00:17:46.840 --> 00:17:49.580
And just to give people a sense, this is like three or four pages in the book.

00:17:49.580 --> 00:17:51.620
It's not a huge, long chapter on it, right?

00:17:51.680 --> 00:17:55.640
These are really quick little things out of many, many recipes.

00:17:55.640 --> 00:17:56.700
So, that's very cool.

00:17:56.700 --> 00:18:01.140
Another one has to do with temporary objects.

00:18:01.140 --> 00:18:10.220
So, what people often do is if they've got to load up a bunch of arbitrary things, they'll stuff them in a dictionary, put the values in there, and pass them around.

00:18:10.980 --> 00:18:13.560
But those don't behave like objects.

00:18:13.560 --> 00:18:15.960
I can't say container.value.

00:18:15.960 --> 00:18:21.040
I have to say container.bracket value or container.get quote value, things like that, right?

00:18:21.040 --> 00:18:28.500
So, it kind of breaks this idea of these truly flexible objects that you can just pass around in Python, right?

00:18:28.500 --> 00:18:29.080
Yeah.

00:18:29.080 --> 00:18:37.460
That's surprisingly one of the most frequent complaints that I hear from, like, new Python users.

00:18:37.780 --> 00:18:40.460
They're still not well-versed in the language.

00:18:40.460 --> 00:18:43.040
Maybe they are approaching Python for the first time.

00:18:43.040 --> 00:18:52.140
And they frequently came to me saying, hey, but I could do these in maybe JavaScript when I can just declare an object on the fly and assign attributes.

00:18:52.140 --> 00:18:57.460
Yeah, probably a C++ and a C# and a Java developer wouldn't have this complaint.

00:18:57.460 --> 00:18:58.400
No, yeah.

00:18:58.400 --> 00:18:58.980
Right?

00:18:58.980 --> 00:19:00.740
Yeah, absolutely.

00:19:00.740 --> 00:19:05.760
And actually, in Python, that's not hard at all.

00:19:05.760 --> 00:19:07.980
You can do the same exact thing in Python.

00:19:07.980 --> 00:19:15.960
And there is a fast way to implement an object that allows you to do the same exact thing.

00:19:15.960 --> 00:19:21.780
You can implement your own bunch objects, which are able to store anything in a dictionary.

00:19:21.780 --> 00:19:27.600
Because in the end, it is nearly the same as a standard Python object works.

00:19:27.600 --> 00:19:31.040
Because we store attributes in a dictionary.

00:19:31.760 --> 00:19:34.940
But you can access them to the dot notation.

00:19:34.940 --> 00:19:38.520
So by saying object dot whatever attribute you want.

00:19:38.520 --> 00:19:48.320
One of the really interesting things is that if you don't have too many, too much problems of using a model that is made for doing totally different things,

00:19:48.320 --> 00:19:54.720
you can even use the arc parse dot namespace class, which does exactly that.

00:19:54.900 --> 00:20:02.760
But it won't be immediately obvious to other people that read your code what are you doing and why you import a namespace for parsing arguments.

00:20:02.760 --> 00:20:07.740
But it allows you to accept any property to the dot notation.

00:20:07.740 --> 00:20:08.060
Yeah.

00:20:08.060 --> 00:20:09.900
So in here, you define a class.

00:20:09.900 --> 00:20:15.280
You call it a bunch, which is like just an object that can be extended with arbitrary values.

00:20:15.280 --> 00:20:22.280
And the trick is to have it derived from dictionary, have it implement dunder get attribute and dunder set adder.

00:20:22.280 --> 00:20:29.140
And then it just reads from and writes to the internal dictionary and converts key errors to attribute errors.

00:20:29.140 --> 00:20:32.500
And you have these little anonymous objects you can just create.

00:20:32.500 --> 00:20:33.260
Yes, exactly.

00:20:33.260 --> 00:20:38.100
And one interesting benefit is also that it inherits from a dictionary.

00:20:38.100 --> 00:20:45.480
You can set all the attributes at the beginning in a single step if you want, because you can just provide them as arguments to the dictionary.

00:20:45.940 --> 00:20:46.380
Right.

00:20:46.380 --> 00:20:52.060
It basically has like a built-in keyword argument initializer you could just use, right?

00:20:52.060 --> 00:20:52.260
Yeah.

00:20:52.260 --> 00:20:53.080
Which is cool.

00:20:53.080 --> 00:20:57.720
The one trick that I thought was nice, I had seen the get attribute and set adder before.

00:20:57.940 --> 00:21:05.100
But one of the problems is if you go and ask what type it is, you know, type of or type, parentheses, the thing.

00:21:05.100 --> 00:21:05.320
Yeah.

00:21:05.320 --> 00:21:07.440
It'll always say bunch, right?

00:21:07.440 --> 00:21:17.080
But you showed a way to like extend this just a little bit so it'll actually report whatever name you want it to like in a traceback or in a, you know, rep or things like that.

00:21:17.080 --> 00:21:32.160
Yes, you can extend the bunch to report the name you need so that when you receive a traceback or something like that, as you pointed out, you'll read that it's actually a user, for example, and not just a bunch that you never know what's contained.

00:21:32.160 --> 00:21:36.380
And you can actually extend that even further to pass type checks.

00:21:36.380 --> 00:21:46.540
If you are, for example, trying to use them to emulate some other object or things like that, you can actually extend it to pass the type checks for the other objects.

00:21:46.540 --> 00:21:49.300
So it can be pretty flexible and convenient.

00:21:49.300 --> 00:21:50.380
Yeah, I really like it.

00:21:50.380 --> 00:21:59.140
So what are some of the use cases where you might choose to use that over a dictionary or over a custom, more fixed structure class or something like that?

00:21:59.140 --> 00:22:06.380
Well, for example, the bunch is something that I use frequently when experimenting, when writing prototypes or things like that.

00:22:06.380 --> 00:22:12.940
I don't want to go and declare the whole hierarchy of classes before I even have clear the idea of what I'm trying to do.

00:22:12.940 --> 00:22:18.060
So I frequently end up relying on bunch for that purpose.

00:22:18.060 --> 00:22:28.180
And also, for example, in TurboGear itself, they are used to keep around some user-provided values that you don't know what the user is going to store there.

00:22:28.320 --> 00:22:32.320
You don't know what the user is going to need to keep around and things like that.

00:22:32.320 --> 00:22:40.420
And you want to allow the user a quick access to those attributes without having to look up in addition or things like that.

00:22:40.420 --> 00:22:47.020
So it's generally very convenient when you really don't know yet what you are going to store in that object.

00:22:47.020 --> 00:22:47.340
Right.

00:22:47.340 --> 00:22:50.640
Or it's determined at runtime in bizarre ways, right?

00:22:50.640 --> 00:22:53.880
Like, you know, I think of like a CSV file.

00:22:53.880 --> 00:22:56.940
And maybe that's some common stuff.

00:22:57.040 --> 00:23:00.800
But other things you don't know if they're there and you just want to put it all, you know, load it all up, right?

00:23:00.800 --> 00:23:03.620
You just gave me a pretty interesting idea, actually.

00:23:03.620 --> 00:23:12.500
It would be easy to extend anything that loads additionally, like a JSON parser, for example, to output bunches.

00:23:12.500 --> 00:23:17.600
So you can access the properties in a normal way instead of having to look up in addition.

00:23:17.840 --> 00:23:18.340
I like it.

00:23:18.340 --> 00:23:27.240
And, you know, we'll talk, you know, at least in your book, you cover like default values and avoiding all the tests, you know, is this key in this dictionary?

00:23:27.560 --> 00:23:34.360
And you could probably combine those to come up with a pretty clean API for interacting with data exchange of all sorts.

00:23:34.360 --> 00:23:34.720
Yeah.

00:23:34.720 --> 00:23:35.000
Cool.

00:23:35.480 --> 00:23:40.040
So one of the next ones that you talked about is templating.

00:23:40.040 --> 00:23:44.640
Now, Python has lots of templating outside the standard library.

00:23:44.640 --> 00:23:45.760
We've got Jinja 2.

00:23:45.760 --> 00:23:47.660
We've got Chameleon templates.

00:23:47.660 --> 00:23:51.380
You can, like, write your own if you really want to go crazy.

00:23:51.640 --> 00:23:57.700
But all those are both external packages with many dependencies and often just found in the web and things like that.

00:23:57.700 --> 00:24:10.600
But what I was surprised to see is you can actually extend what string.format means with your own implementation and sort of create your own little mini templating language like Jinja 2 or something.

00:24:10.600 --> 00:24:11.640
Yes, absolutely.

00:24:11.640 --> 00:24:16.840
That's actually one of the recipes that I used for many years.

00:24:16.980 --> 00:24:28.460
And now I was not sure about which way to propose the best implementation because there are new ways you can achieve the same result in Python 3.6.

00:24:28.460 --> 00:24:35.420
For example, to the f-strings, you can actually write code in an f-string itself and it would be evaluated.

00:24:35.420 --> 00:24:41.140
But for the previous version of Python, that was not available.

00:24:41.140 --> 00:24:47.500
And also the kind of code that you can write in f-string is limited to expression so that you can evaluate.

00:24:47.500 --> 00:25:00.900
While it's not really suggested, but by subclassing the string formatter and specifying it, you can actually make it able to run any kind of code that you provided in the template.

00:25:00.900 --> 00:25:05.220
Like even define a function and call it from your template and things like that.

00:25:05.420 --> 00:25:20.620
The trick is interesting because I didn't know myself up to like recent years that the string formatter can actually, whenever you write something between the brackets, it calls the method to look up that value.

00:25:20.620 --> 00:25:31.160
So you can actually write anything within the brackets and it will always ask to that method, what's the value of this thing that the user brought within the brackets.

00:25:31.160 --> 00:25:37.560
And at that point, by subclassing it and driving that method, you can make anything happen.

00:25:37.560 --> 00:25:48.880
Like you can even take code that was written within the brackets and run it and put back in place of the brackets the result of the code execution and things like that.

00:25:48.880 --> 00:25:50.080
Yeah, that's a really great trick.

00:25:50.080 --> 00:26:00.800
So you basically have a little miniature template language just in a string and you just say format, here are the values and boom, it comes out just like you would.

00:26:00.800 --> 00:26:04.440
And you even have what is effectively like a loop, right?

00:26:04.440 --> 00:26:10.460
You give it a list of messages and it can basically iterate over them using string.join.

00:26:10.460 --> 00:26:27.720
Yeah, so in the specific example that I did in the book for the recipe, it tries to showcase that you can even achieve loops because you actually have list compressions or you can have join or depends on the kind of output that you need to do.

00:26:27.720 --> 00:26:42.900
But you can run a list compression within the template or you can run a join within the template and so output a result for multiple entries that depend on the container that you provided.

00:26:43.100 --> 00:26:43.680
It's really cool.

00:26:43.680 --> 00:26:44.800
I think this is a great example.

00:26:44.800 --> 00:26:53.120
And so often you see people sort of imperatively building up strings in code and it's just, it doesn't seem like a great way to do it.

00:26:53.120 --> 00:26:54.580
It can't be great for performance.

00:26:54.580 --> 00:26:58.440
It's not clear really what the output is supposed to be sometimes.

00:26:58.880 --> 00:27:00.600
And this way I think is a lot nicer.

00:27:00.600 --> 00:27:01.720
So quite cool.

00:27:01.720 --> 00:27:15.900
Yeah, of course, because you can have the wall, you can have a better idea of the wall output that you are going to generate because you see it on the fly instead of having to go through tons of if cases and string concatenations and things like that.

00:27:15.900 --> 00:27:21.580
You could have basically a, you know, a multi-line string with more or less the exact shape of what's going to be outputted.

00:27:21.580 --> 00:27:21.780
Yeah.

00:27:21.780 --> 00:27:23.180
It's just the place is going to be filled in.

00:27:23.180 --> 00:27:24.100
It's really nice.

00:27:24.100 --> 00:27:27.360
Much like a chameleon template or something like that, right?

00:27:27.360 --> 00:27:28.680
A simplified version.

00:27:28.840 --> 00:27:30.020
It's not as powerful.

00:27:30.020 --> 00:27:30.480
Yeah.

00:27:30.480 --> 00:27:34.180
But it's already can satisfy most of the needs that you might have.

00:27:34.180 --> 00:27:34.460
Yeah.

00:27:34.460 --> 00:27:38.340
And, you know, this is one of those cases where I'm like, I had no idea you could do this.

00:27:38.340 --> 00:27:39.120
This is quite cool.

00:27:39.120 --> 00:27:48.780
There was a conversation on Twitter recently where somebody sent out a poll and said, what percentage of the standard library do you think you know?

00:27:48.780 --> 00:27:54.900
And it was, you know, zero to 20, 20 to 40%, 40 to 60, and so on.

00:27:54.900 --> 00:27:58.800
And they copied me thinking, I don't know, maybe they're thinking I would check like, oh, yeah, like 90%.

00:27:58.800 --> 00:28:00.240
But it's because of this stuff.

00:28:00.240 --> 00:28:03.000
I'm thinking like 40% is the right answer.

00:28:03.000 --> 00:28:05.920
Because I know how to do the really common stuff super well.

00:28:05.920 --> 00:28:10.460
But there's all these little extra amazing things that it's like, I didn't even know that existed.

00:28:10.460 --> 00:28:11.940
And so, for example.

00:28:12.360 --> 00:28:13.460
Yeah, I totally agree with you.

00:28:13.460 --> 00:28:14.600
Maybe I'll ask you, what do you think?

00:28:14.600 --> 00:28:16.740
What percentage of the standard library do you think you know?

00:28:16.740 --> 00:28:18.940
And after writing this book, maybe that number went up a lot.

00:28:18.940 --> 00:28:20.320
Oh, yeah, absolutely.

00:28:20.320 --> 00:28:28.520
I think that that's a really hard question because I believe I probably don't go over the 70%, you know?

00:28:28.520 --> 00:28:33.840
And I wrote a book about it because there are so many things that are hidden inside.

00:28:33.840 --> 00:28:40.440
There's probably not even people that wrote the standard library know every way you can use it, you know?

00:28:40.440 --> 00:28:40.740
Yeah.

00:28:40.740 --> 00:28:41.960
It's pretty incredible.

00:28:41.960 --> 00:28:43.980
Anyway, back to the topics.

00:28:43.980 --> 00:28:47.260
This next one also falls into that, oh, that is so cool.

00:28:47.260 --> 00:28:50.040
I didn't realize this was around for us.

00:28:50.040 --> 00:28:52.420
So I know about working with memory.

00:28:52.420 --> 00:28:54.040
I know about working with files.

00:28:54.780 --> 00:29:01.260
But you have this cool example of I would like to basically cache something in memory or load it into memory.

00:29:01.260 --> 00:29:07.460
But if it gets too big, I need to switch what I'm doing, maybe save it to a file and start reading it from there.

00:29:07.460 --> 00:29:12.820
Because, well, if it's 20 gigs, that's probably not going to work out super well most of the time, right?

00:29:12.820 --> 00:29:13.900
So tell us about this.

00:29:13.900 --> 00:29:17.320
So that's one of my favorite tools in the standard library.

00:29:17.540 --> 00:29:26.440
When I need to, like, keep around some data, temporary data, maybe a file that the user uploaded to me or something,

00:29:26.440 --> 00:29:31.840
or something I'm generating, like think of an image you are reciting or things like that.

00:29:31.840 --> 00:29:39.160
It makes sense to do that in memory because it's the fastest option, so it's usually the best way to go.

00:29:39.160 --> 00:29:44.520
But at a certain point, if you really don't know the size of the input that you are going to receive,

00:29:45.000 --> 00:29:52.380
if you are reciting, like, 300 pixels image, it can fit in the memory of every computer.

00:29:52.380 --> 00:29:58.840
But if you are reciting 5 gigabytes of JPEG, it can start to be a big problem.

00:29:58.840 --> 00:30:06.400
And that's the point where having something, a data structure that automatically switches from memory to disk

00:30:06.400 --> 00:30:09.800
on a specified threshold can be very convenient.

00:30:09.800 --> 00:30:15.460
And that's exactly what the temporary file spooled temporary file class does.

00:30:15.460 --> 00:30:22.240
You create one, and at the beginning, everything is in memory up to a point where it grows so big

00:30:22.240 --> 00:30:25.220
that it goes over a threshold that you choose.

00:30:25.220 --> 00:30:30.880
And when the threshold is surpassed, everything switches to disk, and you don't consume memory anymore.

00:30:30.880 --> 00:30:31.720
That is so cool.

00:30:31.720 --> 00:30:39.740
So this is the spooled temporary file, and you just give it a maximum size, and you work with it like a regular file.

00:30:39.740 --> 00:30:42.760
You can write from it, seek on it, read from it.

00:30:42.760 --> 00:30:47.780
And either it stays in memory, or if it turns out that it was got to be too big,

00:30:47.780 --> 00:30:50.540
then it just, you know, writes itself to disk and streams off the disk.

00:30:50.540 --> 00:30:51.160
That's cool.

00:30:51.320 --> 00:30:52.100
Yeah, absolutely.

00:30:52.100 --> 00:31:00.900
Because I frequently saw this pattern implemented by many projects, like use byte.io if the size is small,

00:31:00.900 --> 00:31:03.500
and switch to temporary file if it's big.

00:31:03.500 --> 00:31:08.940
But it can actually be done for you without having to write any code if you use a spooled temporary file.

00:31:08.940 --> 00:31:09.540
This is cool.

00:31:09.780 --> 00:31:15.020
To me, it feels like that's something you see a lot in Python when people come from another language

00:31:15.020 --> 00:31:21.440
or some other technology where they're like, oh, I need to do this thing, so I'm going to implement it from scratch

00:31:21.440 --> 00:31:25.640
when it could just be temp file dots, well, temporary file.

00:31:25.640 --> 00:31:26.920
You don't need to implement it.

00:31:26.920 --> 00:31:29.520
It's done, and it's already tested, and it's fast, right?

00:31:29.520 --> 00:31:33.200
I think that's part of knowing the standard library well, right?

00:31:33.200 --> 00:31:39.560
Yeah, and it cannot only make your life easier because it's, of course, probably more tested

00:31:39.560 --> 00:31:43.720
and been around for years and more robust and things like that.

00:31:43.720 --> 00:31:50.500
But it can usually be actually faster because many parts of the standard library are implemented in C,

00:31:50.500 --> 00:31:55.340
so they can be far faster than the code that you've wrote yourself in Python.

00:31:55.340 --> 00:32:03.320
So it's usually a very good idea to look up into the standard library before trying to write something from scratch.

00:32:03.320 --> 00:32:07.160
That touches on this idea of is Python fast or slow?

00:32:07.160 --> 00:32:10.860
And I feel often that the answer is both or either.

00:32:10.860 --> 00:32:12.160
Something like this, right?

00:32:12.160 --> 00:32:17.460
Like you can write code that just runs in pure Python, and it can be very inefficient.

00:32:17.460 --> 00:32:22.260
But as soon as you work with something that just hands off something down to a C layer,

00:32:22.400 --> 00:32:28.700
either that's in the standard library in CPython or if it's in, say, NumPy or something like that,

00:32:28.700 --> 00:32:31.200
like all of a sudden that whole conversation changes.

00:32:31.200 --> 00:32:35.860
And this is a little bit of like you may pick up that advantage automatically

00:32:35.860 --> 00:32:38.040
by just using the standard library stuff better.

00:32:38.040 --> 00:32:42.800
Yeah, I think that it's the only, if not one of the few languages,

00:32:42.800 --> 00:32:46.320
where the pattern on performance is reversed.

00:32:46.320 --> 00:32:51.600
The farther away you stay from the machine, the faster it goes, you know?

00:32:51.880 --> 00:32:52.300
Yes.

00:32:52.300 --> 00:32:56.780
The more you try to work with low-level data, the slower it will be.

00:32:56.780 --> 00:33:01.140
The more you try to work with higher-level data and functions, the faster it will go.

00:33:01.140 --> 00:33:02.640
That's a really good perspective.

00:33:02.640 --> 00:33:03.120
I like it.

00:33:05.500 --> 00:33:08.500
This portion of Talk Python To Me is brought to you by Rollbar.

00:33:08.500 --> 00:33:09.900
Got a question for you.

00:33:09.900 --> 00:33:12.620
Have you been outsourcing your bug discovery to your users?

00:33:12.620 --> 00:33:15.240
Have you been making them send you bug reports?

00:33:15.240 --> 00:33:16.960
You know there's two problems with that.

00:33:16.960 --> 00:33:18.940
You can't discover all the bugs this way.

00:33:18.940 --> 00:33:21.500
And some users don't bother reporting bugs at all.

00:33:21.500 --> 00:33:23.400
They just leave, sometimes forever.

00:33:23.400 --> 00:33:27.200
The best software teams practice proactive error monitoring.

00:33:27.200 --> 00:33:32.160
They detect all the errors in their production apps and services in real time and debug important

00:33:32.160 --> 00:33:35.600
errors in minutes or hours, sometimes before users even notice.

00:33:35.600 --> 00:33:40.700
Teams from companies like Twilio, Instacart, and CircleCI use Rollbar to do this.

00:33:40.700 --> 00:33:46.220
With Rollbar, you get a real-time feed of all the errors so you know exactly what's broken

00:33:46.220 --> 00:33:46.760
in production.

00:33:46.760 --> 00:33:52.120
And Rollbar automatically collects all the relevant data and metadata you need to debug

00:33:52.120 --> 00:33:53.960
the errors so you don't have to sift through logs.

00:33:53.960 --> 00:33:57.900
If you aren't using Rollbar yet, they have a special offer for you, and it's really awesome.

00:33:57.900 --> 00:34:04.680
Sign up and install Rollbar at talkpython.fm/Rollbar, and Rollbar will send you a $100

00:34:04.680 --> 00:34:10.580
gift card to use at the Open Collective, where you can donate to any of the 900-plus projects

00:34:10.580 --> 00:34:14.460
listed under the Open Source Collective or to the Women Who Code organization.

00:34:14.900 --> 00:34:18.100
Get notified of errors in real time and make a difference in open source.

00:34:18.100 --> 00:34:20.980
Visit talkpython.fm/Rollbar today.

00:34:20.980 --> 00:34:27.220
The next pattern that you talk about is with displaying progress bars.

00:34:27.220 --> 00:34:31.140
And for all the Python developers, you know, think when you type pip install a thing and

00:34:31.140 --> 00:34:35.300
you see the little downloading progress bar going across in the terminal or command prompt,

00:34:35.300 --> 00:34:38.260
like those kinds of progress bars, right, on the terminal.

00:34:38.260 --> 00:34:40.120
That's a very common need.

00:34:40.200 --> 00:34:47.180
And actually, I came up with this recipe because I wanted to showcase a more general need that

00:34:47.180 --> 00:34:53.240
frequently you have when writing terminal text-based software, which is you really don't know the

00:34:53.240 --> 00:34:55.580
environment where you are going to run.

00:34:55.580 --> 00:35:02.320
You know, maybe my user has a terminal which is very small or maybe a full screen or things

00:35:02.320 --> 00:35:02.800
like that.

00:35:02.800 --> 00:35:08.920
So when you try to provide your output, it's always hard to find the right balance of how

00:35:08.920 --> 00:35:13.420
much I should write, how much I should write on a single line, when should I go to a new line,

00:35:13.420 --> 00:35:14.340
and things like that.

00:35:14.340 --> 00:35:20.480
And the progress bar is a perfect example for that because it should always consume as much

00:35:20.480 --> 00:35:26.380
space as available and write the progress on top of that, you know.

00:35:26.820 --> 00:35:33.000
So it was a perfect case to showcase that in the final library, you have tools that allow

00:35:33.000 --> 00:35:36.960
you to inspect the terminal, understand what's the size.

00:35:36.960 --> 00:35:42.460
And we very, like, I don't remember the specific length of the recipe, but I think we are talking

00:35:42.460 --> 00:35:45.140
about, like, less than 10 lines of code.

00:35:45.140 --> 00:35:50.800
You can write a fully functional progress bar that fits and adapts to the size of the screen

00:35:50.800 --> 00:35:51.700
and things like that.

00:35:51.700 --> 00:35:53.080
Yeah, it's a great little example.

00:35:53.080 --> 00:35:58.660
And of course, there are things like TQDM if you want to go outside, right, which is a

00:35:58.660 --> 00:35:59.380
cool progress bar.

00:35:59.380 --> 00:36:04.300
But that's not the same as I have no dependencies and I still have a progress bar, which is pretty

00:36:04.300 --> 00:36:04.500
cool.

00:36:04.500 --> 00:36:05.140
Yeah, exactly.

00:36:05.140 --> 00:36:11.900
Yeah, if you want to cover all the cases, like things like TQDM are very cool when I use

00:36:11.900 --> 00:36:14.380
it in some projects, I think it works great.

00:36:14.380 --> 00:36:20.340
But, you know, one thing is using five lines of code function and the other is bringing

00:36:20.340 --> 00:36:22.100
in a whole library.

00:36:22.100 --> 00:36:22.620
For sure.

00:36:22.620 --> 00:36:28.960
One of the things I liked about this recipe is you used a lot of cool parts of Python.

00:36:28.960 --> 00:36:33.740
So you have a nice decorator that you put onto a function.

00:36:34.200 --> 00:36:41.700
And that function long as it is a generator that returns numbers that can drive the progress

00:36:41.700 --> 00:36:41.960
bar.

00:36:41.960 --> 00:36:47.520
So basically, you do your work and you're looping through it and the function can just periodically

00:36:47.520 --> 00:36:51.800
yield out a number, which is one to 100, which is the progress, right?

00:36:51.800 --> 00:36:52.860
That's a great pattern.

00:36:53.040 --> 00:36:53.680
Yeah, absolutely.

00:36:53.680 --> 00:36:58.740
I think it's very convenient compared to, like, the most common solution that I saw around

00:36:58.740 --> 00:37:05.660
when implementing progress bar is actually to go for a territory that you, like, throw up

00:37:05.660 --> 00:37:07.740
around the original data source.

00:37:07.740 --> 00:37:14.380
And at that point, it computes the site itself, but that requires that you already have all the

00:37:14.380 --> 00:37:15.020
data available.

00:37:15.020 --> 00:37:21.540
Like, I can apply that pattern to a list because I know that a list has five elements, so I can

00:37:21.540 --> 00:37:24.100
compute how much progress I should do for each element.

00:37:24.100 --> 00:37:29.900
But I cannot apply that to a generator because I might not know how much data is going to

00:37:29.900 --> 00:37:30.940
generate a generator.

00:37:30.940 --> 00:37:37.460
And with this pattern is the best balance, in my opinion, because you just yield the progress

00:37:37.460 --> 00:37:38.040
yourself.

00:37:38.040 --> 00:37:39.940
It doesn't matter what you are iterating.

00:37:39.940 --> 00:37:42.080
It can be in the network input.

00:37:42.080 --> 00:37:43.560
It can be on a generator.

00:37:43.560 --> 00:37:44.700
It can be on a list.

00:37:44.700 --> 00:37:45.600
It doesn't really matter.

00:37:45.600 --> 00:37:50.740
It's just the great you're financial with the progress bar decorator and yield the progress

00:37:50.740 --> 00:37:51.860
that you want to report.

00:37:51.860 --> 00:37:52.820
I really like it.

00:37:52.820 --> 00:37:53.840
And it's quite cool.

00:37:53.840 --> 00:38:03.400
So the next one has to do with the overall safety or consistency of a block of code.

00:38:03.400 --> 00:38:10.760
Now, a lot of times people think if they have a try and they have an accept catching block,

00:38:10.760 --> 00:38:14.600
they've handled the exception correctly and everything is fine.

00:38:15.020 --> 00:38:17.580
But that's sometimes true.

00:38:17.580 --> 00:38:23.040
But a lot of times there's an iterative or multi-step process where a bunch of changes

00:38:23.040 --> 00:38:27.140
have to be made and either all the changes should be applied or none of the changes should

00:38:27.140 --> 00:38:27.560
be applied.

00:38:27.560 --> 00:38:29.860
You know, think of a database transaction, right?

00:38:29.860 --> 00:38:30.620
That's very common.

00:38:30.620 --> 00:38:32.900
Either you get to the end or you commit it or roll it back.

00:38:32.900 --> 00:38:36.760
But that same thing applies in other persistent things.

00:38:36.760 --> 00:38:40.680
Even in memory, actually, if you're changing different parts in the memory of your app, you should

00:38:40.680 --> 00:38:41.300
consider this.

00:38:41.540 --> 00:38:44.160
But this next recipe has to deal with files.

00:38:44.160 --> 00:38:49.760
So if I'm going to be making multiple changes to a file or I want to make sure I write all

00:38:49.760 --> 00:38:52.460
the file or none of the file, how do I do that?

00:38:52.540 --> 00:38:58.980
That's actually a very good question because whenever you handle exceptions, it's obvious that you are

00:38:58.980 --> 00:39:01.020
handling the exception in your own code.

00:39:01.020 --> 00:39:05.420
But it's less obvious that there might be side effects from that exception.

00:39:05.420 --> 00:39:12.820
Like in the case you were writing a file and you fail at a certain point, maybe it's a deep space or something

00:39:12.820 --> 00:39:13.360
like that.

00:39:13.760 --> 00:39:20.720
What you've wrote so far, it's already there, was already written up to the point where the

00:39:20.720 --> 00:39:23.080
candle flashed the bright and so on.

00:39:23.080 --> 00:39:25.880
So you are not really handling the exception.

00:39:25.880 --> 00:39:26.260
Yeah.

00:39:26.260 --> 00:39:30.700
And even if it's not like you run out of disk space, it could be I would need to write 20

00:39:30.700 --> 00:39:32.940
things to a file in the 10th wall.

00:39:32.940 --> 00:39:33.200
Yeah.

00:39:33.200 --> 00:39:36.180
Something is none and I didn't expect it to be and it crashed.

00:39:36.180 --> 00:39:36.620
Right.

00:39:36.660 --> 00:39:39.640
So it sort of just bailed out halfway through.

00:39:39.640 --> 00:39:39.860
Right.

00:39:39.860 --> 00:39:41.840
Which is how do you know that's going to happen?

00:39:41.840 --> 00:39:42.900
How do you recover from it?

00:39:42.900 --> 00:39:49.280
The way you recover is actually not super complex and it can be made a very small tool.

00:39:49.280 --> 00:39:54.800
And in the book, there is this recipe which is called Safe Open.

00:39:54.800 --> 00:39:59.300
So that allows you to open a file in a safe way for writing.

00:39:59.520 --> 00:40:06.760
And whenever you Safe Open, all the writes that you do to the file happen all or nothing.

00:40:06.760 --> 00:40:11.140
So if everything succeeded, all the writes happen.

00:40:11.140 --> 00:40:17.100
If something fails, like you have an exception in the middle between three lines of code that

00:40:17.100 --> 00:40:23.280
draw two pieces, then nothing was written because everything is recovered to the previous

00:40:23.280 --> 00:40:23.660
phase.

00:40:23.660 --> 00:40:26.220
And that's actually done based on...

00:40:26.220 --> 00:40:27.200
I was just going to say, I love it.

00:40:27.200 --> 00:40:28.680
That is such a great thing.

00:40:28.680 --> 00:40:34.680
Like you write, you just write exactly the same code to write to a file, use Safe Open

00:40:34.680 --> 00:40:35.380
instead of Open.

00:40:35.380 --> 00:40:40.280
And then either the file doesn't even exist if there's an error or it's completely consistent

00:40:40.280 --> 00:40:40.840
at the end.

00:40:40.840 --> 00:40:45.340
And this, just to be clear, the Safe Open is a thing that you created in your recipe, but

00:40:45.340 --> 00:40:46.740
it's pretty straightforward, right?

00:40:46.740 --> 00:40:47.280
Yeah.

00:40:47.280 --> 00:40:50.420
It's like four lines of code or something like that.

00:40:50.420 --> 00:40:53.820
And it actually was like having a database transaction.

00:40:54.000 --> 00:40:59.160
You start the transaction and you can roll back on committees and the middle nothing happened.

00:40:59.160 --> 00:41:00.240
It's super cool.

00:41:00.240 --> 00:41:06.660
So basically the way it works is it writes to a temporary file and then it uses...

00:41:06.660 --> 00:41:08.460
It is itself a context manager.

00:41:08.460 --> 00:41:09.800
So you put it in a with block.

00:41:09.800 --> 00:41:15.740
If you exit the with block without error, then it will just rename the file to the real one,

00:41:15.740 --> 00:41:16.800
what you're targeting.

00:41:17.000 --> 00:41:22.000
If it exits with an error, it'll just remove the temporary file, which is perfect.

00:41:22.000 --> 00:41:22.980
It's so clean and nice.

00:41:22.980 --> 00:41:27.340
And that works because the rename operation is guaranteed to be atomic.

00:41:27.340 --> 00:41:29.000
So it's all or nothing.

00:41:29.000 --> 00:41:32.760
You replace the old file with the new one or you didn't.

00:41:32.760 --> 00:41:37.000
It's not like when writing that you could have written only part of the output.

00:41:37.180 --> 00:41:38.280
Yeah, it's super.

00:41:38.280 --> 00:41:47.880
Now, this other one is an interesting problem in a more complicated area, but still really good.

00:41:47.880 --> 00:41:54.740
So in Python, we have now, in modern Python, you have basically three ways to do concurrency, right?

00:41:54.740 --> 00:42:04.060
You've got asyncio, which actually uses only one thread and basically releases that thread to do other work

00:42:04.060 --> 00:42:06.360
while you're waiting on like a network or a database or something.

00:42:06.540 --> 00:42:09.220
So that's the asyncio and async and wait keywords.

00:42:09.220 --> 00:42:13.120
Then we have threads and we have multiprocessing.

00:42:13.120 --> 00:42:18.540
And the multiprocessing really exists to get around the limitation of the GIL, the global interpreter lock,

00:42:18.540 --> 00:42:21.300
for computational type stuff, right?

00:42:21.300 --> 00:42:21.940
Mostly.

00:42:21.940 --> 00:42:22.780
Yeah, absolutely.

00:42:22.780 --> 00:42:26.680
That's the only way you can currently work around the GIL and Python

00:42:26.680 --> 00:42:36.780
because all the other solutions that you mentioned suffer from that problem that you can actually run only one operation at a time,

00:42:36.780 --> 00:42:43.200
which is obvious in the case of async because that's the inner behavior of the feature itself.

00:42:43.200 --> 00:42:48.780
But it's less obvious for new users for threads because they expect to be really parallel.

00:42:48.780 --> 00:42:55.540
And that's the way frequently when talking about parallel operations in Python,

00:42:55.540 --> 00:43:01.920
multiprocessing is suggested because that way you can actually go concurrent for real.

00:43:02.320 --> 00:43:06.200
So the way you get around the GIL, the way multiprocessing works is in your code,

00:43:06.200 --> 00:43:11.860
you say, I want to run these functions with these 10 different values in parallel on different,

00:43:11.860 --> 00:43:14.100
you know, on different basically processes.

00:43:14.100 --> 00:43:18.100
And Python will create 10 sub processes or it can pull it up or whatever.

00:43:18.100 --> 00:43:23.440
But the point is there are multiple other Python sub processes doing the work.

00:43:23.440 --> 00:43:26.120
And then you get the answer back.

00:43:26.120 --> 00:43:29.520
But the challenge can be if I'm doing threading or asyncio,

00:43:29.520 --> 00:43:33.980
I can just have an object and share it and change it with all the different parts of my code.

00:43:33.980 --> 00:43:37.720
In multiprocessing, that memory is not shared by default.

00:43:37.720 --> 00:43:44.320
So then how do you interact with like if different parts of my multiprocessing thing are generating data

00:43:44.320 --> 00:43:45.380
and they need access to it?

00:43:45.380 --> 00:43:46.080
Like what do I do?

00:43:46.220 --> 00:43:48.860
Yeah, there are many tools in Python for doing that.

00:43:48.860 --> 00:43:52.520
But I think that the most powerful one is the multiprocessing manager.

00:43:52.520 --> 00:43:59.560
And it's also there are very few people that use it on every day and then know it exists and things like that.

00:43:59.560 --> 00:44:02.380
And even between those that know it exists,

00:44:02.380 --> 00:44:07.160
even less people know that you have some very cool features.

00:44:07.160 --> 00:44:11.880
Like you can actually replace a whole database system with multiprocessing manager

00:44:11.880 --> 00:44:20.800
because not only it allows you to share the values that you want across the processes that all forked from the same parent.

00:44:20.800 --> 00:44:27.140
So when like using writing a multiprocess program that forks multiple children to do some job.

00:44:27.580 --> 00:44:33.200
But it also allows you to share data across processes that have nothing in common.

00:44:33.200 --> 00:44:39.280
They were started on totally different times, different places, on different machines even.

00:44:40.280 --> 00:44:47.120
Because the way the multiprocessing manager works is that it actually allows you to listen on a port.

00:44:47.120 --> 00:44:51.460
So it works on a plain TCPIP network protocol.

00:44:51.460 --> 00:45:02.320
And you fetch and store values in the multiprocessing manager by picking them to this object and sending them across the network.

00:45:02.320 --> 00:45:10.240
So if you think about that, it's practically like having something like Redis or any other key value store in Python itself.

00:45:10.240 --> 00:45:11.300
It's already there.

00:45:11.420 --> 00:45:12.160
That's a great analogy.

00:45:12.160 --> 00:45:13.640
That's exactly what I was thinking.

00:45:13.640 --> 00:45:18.640
I'm like, this is like a little baby Redis that your process creates, right?

00:45:18.640 --> 00:45:19.580
Yeah, exactly.

00:45:19.580 --> 00:45:20.200
Yeah.

00:45:20.200 --> 00:45:21.220
It's pretty interesting.

00:45:21.220 --> 00:45:28.280
And the big benefit is the shared data can be changed and created over time.

00:45:28.280 --> 00:45:34.800
So, for example, the other things like queues and whatnot that you can use for multiprocessing, you have to create these values up front.

00:45:34.800 --> 00:45:36.780
And they can get more tricky, right?

00:45:36.800 --> 00:45:44.740
So this lets you start and stop processes and the values sort of persist across it and they can be created after by the subprocesses.

00:45:44.740 --> 00:45:49.660
It's like basically a little sub database or a key value store that is just live, right?

00:45:49.660 --> 00:45:57.300
It's like having a dictionary that is shared by all your processes and they can look for values there or new values there.

00:45:57.820 --> 00:46:03.140
And I think that it's very cool that it can be easily adapted to work across the network.

00:46:03.140 --> 00:46:12.740
So you can actually buy really multiprocessing tools and even distribute it across machine and continue to work using the same multiprocessing manager.

00:46:12.740 --> 00:46:14.080
Yeah, that's pretty interesting.

00:46:14.580 --> 00:46:18.140
So the next one that we're going to cover is a little bit in this realm.

00:46:18.140 --> 00:46:24.520
Like I want this thing that I know I could go create a real server for, but I just want to keep it nice and simple, right?

00:46:24.520 --> 00:46:29.700
We could go get real Redis, but now you have a big infrastructure thing instead of a single Python file, right?

00:46:30.040 --> 00:46:39.160
And similarly here, like obviously, you know, you have Turbogears, we've got Flask, Pyramid, Django, all these different frameworks that we could go.

00:46:39.160 --> 00:46:42.440
And they're big, huge dependencies, right?

00:46:42.440 --> 00:46:45.100
They have dependencies of dependencies of dependencies to run.

00:46:45.100 --> 00:46:54.160
But this next example shows you can actually create a non-trivial but somewhat basic WSGI HTTP server, right, in Python.

00:46:54.160 --> 00:46:55.280
Yes, absolutely.

00:46:55.720 --> 00:47:00.280
That's actually, in my opinion, one of the reasons why Python has so many web frameworks.

00:47:00.280 --> 00:47:08.060
You cited like four or five, but there are like, I don't know, a hundred web frameworks in Python or something like that.

00:47:08.060 --> 00:47:08.240
Yes, there's many more.

00:47:08.240 --> 00:47:09.180
We could go for a while.

00:47:09.180 --> 00:47:10.680
Yeah, exactly.

00:47:10.680 --> 00:47:21.700
And the reason, in my opinion, is that because you have so many building blocks already in the Python library that it takes no more than a weekend to create your own web framework, you know?

00:47:22.340 --> 00:47:28.120
And so when you start adding more complex features, of course, it gets far more complex than that.

00:47:28.120 --> 00:47:39.300
But for rolling out a framework framework that it's able to route requests to functions or classes and send them back, it takes no more than a few hours of work.

00:47:39.300 --> 00:47:46.620
And actually, if you already know how to do that, it's like a very few lines of code, like 10, 20 lines of code.

00:47:46.720 --> 00:47:53.420
You can achieve everything you need, including routing, requests, handling, serving responses, and everything.

00:47:53.420 --> 00:48:07.700
Yeah, that's interesting because when I thought of what built-in HTTP servers are there for the standard library, I was thinking, okay, so I can create up and just listen for an HTTP request.

00:48:07.700 --> 00:48:09.620
That's pretty easy with sockets and stuff.

00:48:09.620 --> 00:48:16.980
But the whole routing and all that kind of response stuff, I didn't realize was that easy to add on, right?

00:48:16.980 --> 00:48:19.920
Like, much of it is already built in and things like that.

00:48:20.060 --> 00:48:20.740
Yeah, absolutely.

00:48:20.740 --> 00:48:36.980
And not only, you don't even need to go as far as handling the requests yourself because actually there is a fully working application server in the standard library itself that by default works to single-threaders.

00:48:36.980 --> 00:48:40.440
So you will not be able to serve more than one request at a time.

00:48:40.500 --> 00:48:46.040
But there is actually another mix-in in the standard library that you can apply to that class to make it multi-threaded.

00:48:46.040 --> 00:48:56.180
So you get a fully functional multi-threaded application server with just one single line of code and only using what's available in the standard library.

00:48:56.180 --> 00:48:57.360
That's really cool.

00:48:57.360 --> 00:49:06.060
At that point, the only part you need to add on top of that is the routing, which is very easy to roll out using regular expressions lookup.

00:49:06.160 --> 00:49:06.480
Absolutely.

00:49:06.480 --> 00:49:08.500
And you cover that in this simple little one.

00:49:08.500 --> 00:49:14.120
I guess one other thing that it doesn't do is it doesn't serve static files necessarily, right, in this first example.

00:49:14.120 --> 00:49:17.220
But your very next recipe is, and here's how you serve static files.

00:49:17.220 --> 00:49:18.440
Yeah, absolutely.

00:49:18.440 --> 00:49:19.940
That's very easy.

00:49:19.940 --> 00:49:24.340
So you can actually be extended in just a bunch of lines, of course.

00:49:24.340 --> 00:49:24.840
Quite cool.

00:49:24.840 --> 00:49:25.500
All right.

00:49:25.500 --> 00:49:32.620
So another one that we might want to do in the same general area is creating HTML.

00:49:32.620 --> 00:49:42.100
So if you're doing basic stuff like this and you have any user input or you just are going to stick it into some other template system,

00:49:42.100 --> 00:49:49.260
basically if you are generating HTML, you need to be really careful about untrusted HTML.

00:49:49.260 --> 00:49:55.700
Like if you accept user input or the data is based on user input, they could, instead of putting their first name,

00:49:55.700 --> 00:50:00.620
they could put, you know, script, hackable JavaScript or other horrible stuff.

00:50:00.620 --> 00:50:04.260
And if you are not careful and escape it, it'll just go in and execute, right?

00:50:04.500 --> 00:50:09.600
That's one of the most boring parts of writing web application in the past,

00:50:09.600 --> 00:50:13.300
that you have to like to manually go and escape everything yourself.

00:50:13.300 --> 00:50:22.460
I saw, like I still remember those huge files of code where like every single string was wrapped in an escape code.

00:50:22.460 --> 00:50:26.880
And that's horrible because you have to do that for every single thing.

00:50:26.980 --> 00:50:32.320
And if you forget even one, that's a big security issue in your code, you know?

00:50:32.320 --> 00:50:32.740
It is.

00:50:32.740 --> 00:50:33.880
And it's not obvious, right?

00:50:33.880 --> 00:50:40.560
Because like you could have regular text or you could have like Unicode characters that mean the text,

00:50:40.560 --> 00:50:41.800
but they don't look like the text.

00:50:41.800 --> 00:50:45.440
There's just like all sorts of weird ways that people could try to sneak through.

00:50:45.440 --> 00:50:47.220
So you don't want to try to do that yourself.

00:50:47.220 --> 00:50:48.320
That's for sure.

00:50:48.320 --> 00:50:50.400
You don't want to have to care.

00:50:50.400 --> 00:50:52.880
I mean, that's something that I always thought.

00:50:52.880 --> 00:51:01.400
There should be some easy way that does that for me always unless I explicitly want to write some HTML in the output.

00:51:01.400 --> 00:51:09.680
And that's essentially what the recipe can do because, again, the string formatter can be extended in many different ways.

00:51:09.680 --> 00:51:17.860
And one of the ways you can modify it is actually by doing the escaping of everything you provide to the formatter.

00:51:17.860 --> 00:51:24.340
So whenever you're bidding in your string, all the variables that you inject in the string can be escaped for you.

00:51:24.340 --> 00:51:25.660
So you don't have to care.

00:51:25.660 --> 00:51:33.360
And so when you're writing your webpage output or your email output or whatever you're trying to send out to the whole world,

00:51:33.360 --> 00:51:37.660
you don't have to care about properly escaping everything yourself.

00:51:37.660 --> 00:51:39.840
The formatter will do that for you.

00:51:39.840 --> 00:51:40.740
Yeah, I really like it.

00:51:40.740 --> 00:51:51.700
You basically just use standard string format and wherever the input, the variable values go, they either get escaped or not escaped based on your pattern.

00:51:51.700 --> 00:51:59.340
You can say you can either mark them as like safe HTML because you want to dynamically generate them, but you need to stick it in there because it's your code.

00:51:59.340 --> 00:52:02.400
Or you're taking user input and that needs to definitely be escaped.

00:52:02.400 --> 00:52:03.300
Yeah.

00:52:03.300 --> 00:52:04.160
It's a cool pattern.

00:52:04.160 --> 00:52:05.080
Yeah, cool.

00:52:05.080 --> 00:52:05.840
All right.

00:52:05.840 --> 00:52:10.660
The very last one that we're going to talk about is tracing code.

00:52:10.660 --> 00:52:13.480
So understanding what code is executed.

00:52:13.480 --> 00:52:24.020
And you had an interesting comment about how it's not just useful for debugging, but it's also really interesting to just understand what a new library does.

00:52:24.020 --> 00:52:27.780
Like if you want to say, I'm going to run this function, what does it do?

00:52:27.780 --> 00:52:32.020
If it could actually show you the sequential Python that it executed, that'd be kind of neat, right?

00:52:32.020 --> 00:52:32.900
Yes, exactly.

00:52:32.900 --> 00:52:37.880
That's one way I most frequently end up using this recipe, actually.

00:52:37.880 --> 00:52:44.820
When I want to see what's going on in a new piece of code, the PDB debugger is not always the...

00:52:44.820 --> 00:52:51.840
It's very good when you have to pinpoint a specific point and understand what's happening right there at that moment.

00:52:52.220 --> 00:53:05.120
But when you want to get a general overview of what that whole package or library or set of functions is doing and how, where things go when you do something.

00:53:05.120 --> 00:53:08.520
So you want to follow the flow of the code itself.

00:53:08.520 --> 00:53:10.080
It's not as easy.

00:53:10.080 --> 00:53:17.100
You end up like spending hours just writing next into the PDB to show you where it goes next.

00:53:17.560 --> 00:53:18.160
Yeah.

00:53:18.160 --> 00:53:18.260
Yeah.

00:53:18.260 --> 00:53:36.000
And what this recipe can do for you is leverage what's available in the standard library to trace the code that Python executed and tell you the output of exit if each code was run and show you the source code that was executed so you can understand.

00:53:36.000 --> 00:53:37.680
Hey, I called this method.

00:53:37.680 --> 00:53:44.660
And by calling that method, I also ended up calling all these other methods and they executed these branches.

00:53:44.660 --> 00:53:47.180
And so that's why I got that answer.

00:53:47.180 --> 00:53:51.620
Now I see, now I understand how I ended up having that response.

00:53:51.620 --> 00:53:52.660
Yeah, I think it's great.

00:53:52.660 --> 00:53:56.860
And you could even see, oh, this library is actually calling into this other library.

00:53:56.860 --> 00:53:57.640
Yeah.

00:53:57.640 --> 00:53:59.140
And then maybe even why.

00:53:59.140 --> 00:54:00.300
Why is this a dependency?

00:54:00.300 --> 00:54:01.380
Oh, I see what it's doing.

00:54:01.380 --> 00:54:02.620
It's using it here.

00:54:02.880 --> 00:54:03.560
Yes, exactly.

00:54:03.560 --> 00:54:05.660
You end up discovering a lot.

00:54:05.660 --> 00:54:11.160
And it's like having something that goes through the source code for you.

00:54:11.160 --> 00:54:19.800
You know, whenever you want to understand a new library, you usually end up going to GitHub or something like that, open the source code and start reading, you know.

00:54:20.220 --> 00:54:27.140
And you see that here it calls that, so I go and look into the source code to where that function is implemented and so on.

00:54:27.140 --> 00:54:29.020
You do all this work yourself.

00:54:29.020 --> 00:54:32.380
But the tracing module can actually do that for you.

00:54:32.380 --> 00:54:36.280
And so it generates a single flow of everything that happens.

00:54:36.280 --> 00:54:44.440
I think that's really cool, a cool way to think of it, because when you open up somebody else's code that you've never seen before, you're like, all right, well, what is important?

00:54:44.440 --> 00:54:45.500
What isn't?

00:54:45.500 --> 00:54:51.660
I'm going to have to sort of sift through this and figure out, okay, it looks like this is where the action is, and I'm going to pay attention to this.

00:54:51.660 --> 00:54:55.180
And it's kind of a little bit of a detective job.

00:54:55.180 --> 00:54:58.700
Whereas this, it only will show you what executed.

00:54:58.920 --> 00:55:03.060
So you can kind of ignore all the other stuff and just see the part that it actually used.

00:55:03.060 --> 00:55:03.780
That's pretty cool.

00:55:03.780 --> 00:55:10.160
Yes, and you just have like to nearly decorate the function that you want to trace, and you will get the output printed.

00:55:10.160 --> 00:55:12.340
So it's very easy to apply.

00:55:12.340 --> 00:55:12.940
That's cool.

00:55:12.940 --> 00:55:15.640
So definitely, we're going to try playing with this as well.

00:55:15.640 --> 00:55:22.000
All right, well, those are the 10 recipes that we chose to talk about because we thought they were pretty cool.

00:55:22.000 --> 00:55:24.380
But there's a bunch of other ones, many, many more.

00:55:24.380 --> 00:55:25.380
How many are in the book?

00:55:25.380 --> 00:55:25.720
Do you know?

00:55:25.720 --> 00:55:35.340
I have to admit that I don't remember the exact number, but there are around like from 10 to 15 recipes for each chapter, and there are 15 chapters.

00:55:35.340 --> 00:55:38.560
So we are more than 100 for sure.

00:55:38.560 --> 00:55:39.540
Definitely more than 100.

00:55:39.540 --> 00:55:42.160
So there's a lot of these types of little things in here.

00:55:42.160 --> 00:55:49.000
And I think this is a really great book, and I'm happy to highlight it because, like I said, I learned a lot just going through this here.

00:55:49.000 --> 00:55:54.920
And I'm sure everyone who checks it out will learn even more because they'll go through all of them, not just the 10.

00:55:55.140 --> 00:56:01.500
Yeah, I really hope that at least a single person by reading the book will say, wow, I didn't know this.

00:56:01.500 --> 00:56:05.380
That was my whole purpose for the whole time I was writing the book.

00:56:05.380 --> 00:56:05.800
That's cool.

00:56:05.800 --> 00:56:11.640
I think after going through all of this book, I'm going to change my answer from 40 to 50% of how much of the standard library I know.

00:56:11.640 --> 00:56:14.060
It's cool.

00:56:14.060 --> 00:56:14.880
It's really great.

00:56:14.880 --> 00:56:15.720
I appreciate the topic.

00:56:15.720 --> 00:56:17.760
So let's leave it there for that.

00:56:17.760 --> 00:56:21.840
But I do have the two final questions for you, and I'm going to change it up just a little bit.

00:56:21.980 --> 00:56:24.820
So if you're going to write some Python code, what editor do you use?

00:56:24.820 --> 00:56:30.060
I usually use PyCharm for most of the big projects editing.

00:56:30.060 --> 00:56:35.860
And for small lacking around, I started using Visual Studio Code recently.

00:56:35.860 --> 00:56:36.600
Yeah, nice.

00:56:36.600 --> 00:56:38.040
That's exactly what I do.

00:56:38.040 --> 00:56:40.740
Yeah, that sounds strange when you tell people.

00:56:40.740 --> 00:56:42.920
They always look at you, Visual Studio.

00:56:42.920 --> 00:56:44.620
No, it's a pretty good editor.

00:56:44.840 --> 00:56:50.880
They are doing a really good job, and they're putting so much energy into the Python space these days.

00:56:50.880 --> 00:56:52.680
So, yeah, I think it's a great answer.

00:56:52.680 --> 00:56:53.000
Yeah.

00:56:53.000 --> 00:56:53.720
All right, that's the editor.

00:56:53.720 --> 00:56:57.120
And like I said, that's basically the same way that I am and using mine.

00:56:57.120 --> 00:57:06.500
Now, I would normally ask you a notable PyPI package, but let's mix it up and talk about a notable standard library module package that you want to just highlight.

00:57:06.700 --> 00:57:08.700
Okay, that's an interesting question.

00:57:08.700 --> 00:57:14.600
There are so many great modules within the standard library that it's really hard to pick one.

00:57:14.600 --> 00:57:22.840
But if I really have to pick a single module, I would say that the logging module is one of the most fascinating ones.

00:57:22.840 --> 00:57:33.840
Not because it can be the one that you will use most often or it's more feature-rich, but because there's so many ways it can be set up, configured.

00:57:34.200 --> 00:57:48.380
There are so many side effects on the things you do that you can go on learning about logging for years, and you will never know everything that the logging module can do, how it interacts, or the configuration format that it supports, and things like that.

00:57:48.380 --> 00:57:49.500
Yeah, that's a great answer.

00:57:49.500 --> 00:57:52.860
I totally agree with you on that, by the way, that just like you could learn it forever.

00:57:52.860 --> 00:57:54.220
You're never done learning it.

00:57:54.220 --> 00:57:55.120
Yeah.

00:57:55.120 --> 00:57:55.380
All right.

00:57:55.380 --> 00:57:57.720
So people are excited about standard library.

00:57:57.720 --> 00:57:59.120
How do they learn more?

00:57:59.120 --> 00:58:00.700
Final call action, what should they do?

00:58:00.700 --> 00:58:03.560
Oh, just go and read the Python source code.

00:58:03.680 --> 00:58:06.020
That's the way that I learned most things.

00:58:06.020 --> 00:58:14.280
You know, you just open the Python repository on GitHub and start reading modules whenever you want to see what's going on and things like that.

00:58:14.280 --> 00:58:30.520
That's really the best way, and it's worked a lot for me, because that's the only way you can actually see the hidden side effects or those functions that you use for years, importing them from the sign library, but you never really know why they're working that way.

00:58:30.520 --> 00:58:39.740
And that's the way you also discover that something like the formatter can be subclassed with its behavior in different ways or things like that.

00:58:39.740 --> 00:58:49.460
Because those more advanced views are not really documented because they are more in internal detail, I would say, of the standard library.

00:58:49.660 --> 00:58:55.780
But once you know that they exist, they've been there for years, and it's pretty safe to leverage them.

00:58:55.780 --> 00:58:56.940
Yeah, that's great.

00:58:56.940 --> 00:59:02.880
I definitely feel like there's a whole bunch of stuff to explore, and even more so after talking with you about this.

00:59:02.880 --> 00:59:03.900
So thanks for being on the show.

00:59:03.900 --> 00:59:04.380
Absolutely.

00:59:04.380 --> 00:59:05.640
Thank you for having me.

00:59:05.640 --> 00:59:06.000
You bet.

00:59:06.000 --> 00:59:06.200
Bye.

00:59:06.200 --> 00:59:06.600
Bye.

00:59:08.000 --> 00:59:10.760
This has been another episode of Talk Python To Me.

00:59:10.760 --> 00:59:16.420
Our guest on this episode was Alessandro Molina, and it's been brought to you by Linode and Rollbar.

00:59:16.420 --> 00:59:20.580
Linode is your go-to hosting for whatever you're building with Python.

00:59:20.580 --> 00:59:24.120
Get four months free at talkpython.fm/linode.

00:59:24.120 --> 00:59:26.020
That's L-I-N-O-D-E.

00:59:26.020 --> 00:59:28.760
Rollbar takes the pain out of errors.

00:59:28.760 --> 00:59:36.060
They give you the context and insight you need to quickly locate and fix errors that might have gone unnoticed, until users complain, of course.

00:59:36.060 --> 00:59:42.500
Track a ridiculous number of errors for free as Talk Python To Me listeners at talkpython.fm/rollbar.

00:59:42.500 --> 00:59:44.840
Want to level up your Python?

00:59:44.840 --> 00:59:49.700
If you're just getting started, try my Python Jumpstart by Building 10 Apps course.

00:59:49.700 --> 00:59:57.860
Or if you're looking for something more advanced, check out our new async course that digs into all the different types of async programming you can do in Python.

00:59:57.860 --> 01:00:02.520
And of course, if you're interested in more than one of these, be sure to check out our Everything Bundle.

01:00:02.520 --> 01:00:04.400
It's like a subscription that never expires.

01:00:04.400 --> 01:00:06.700
Be sure to subscribe to the show.

01:00:06.700 --> 01:00:09.120
Open your favorite podcatcher and search for Python.

01:00:09.120 --> 01:00:10.340
We should be right at the top.

01:00:10.340 --> 01:00:19.340
You can also find the iTunes feed at /itunes, the Google Play feed at /play, and the direct RSS feed at /rss on talkpython.fm.

01:00:19.340 --> 01:00:21.420
This is your host, Michael Kennedy.

01:00:21.420 --> 01:00:22.920
Thanks so much for listening.

01:00:22.920 --> 01:00:23.980
I really appreciate it.

01:00:23.980 --> 01:00:25.720
Now get out there and write some Python code.

01:00:26.960 --> 01:00:46.220
I really appreciate it.

01:00:46.220 --> 01:01:16.200
Thank you.