Learn Python with Talk Python's 270 hours of courses

#197: Modern Python Standard Library Cookbook Transcript

Recorded on Thursday, Jan 10, 2019.

00:00 A recent Twitter poll went around the web, and it asked, what percentage of the Python standard library do you think you know?

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

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

00:16 This episode with Alessandro Molina definitely helped confirm that estimate for me.

00:21 He just published a book entitled Modern Python Standard Library Cookbook,

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

00:30 but you'll be super glad to hear about them on this episode.

00:33 It's Talk Python to Me, episode 197, recorded January 10th, 2019.

00:38 Welcome to Talk Python to Me, a weekly podcast on Python, the language, the libraries, the ecosystem, and the personalities.

00:58 This is your host, Michael Kennedy.

01:00 Follow me on Twitter, where I'm @mkennedy.

01:02 Keep up with the show and listen to past episodes at talkpython.fm, and follow the show on Twitter via at Talk Python.

01:08 This episode is sponsored by Linode and Rollbar.

01:12 Please check out what they're offering during their segments.

01:14 It really helps support the show.

01:15 Alessandro, welcome back to Talk Python.

01:18 Hi, Michael. Thank you.

01:20 Yeah, it's great to have you on the show again.

01:23 It's been a long time since back on episode 35 when we talked about Turbogears,

01:28 and you're back with a new project that I think is really, really cool, a look at the standard library through a modern Python lens,

01:37 which I'm excited to dig into with you.

01:40 But first, maybe just tell us, what have you been up to the last couple of years

01:44 since we heard from you on the show?

01:45 Yeah, actually, I've been mostly doing Python development as usual.

01:51 That's my most prominent interest in open source development.

01:58 I started a bunch of open source projects aside of Turbogears, but I'm still moving Turbogears forward,

02:05 and we actually just released version 2.4, which had a major rewrite of the framework,

02:11 which is great because we are trying to keep it modern.

02:14 The standard library is doing a great job at that.

02:17 Yeah, that's really cool.

02:18 What are some of the other projects that you've been working on, the other open source ones you've released?

02:23 One of the most interesting projects, I think, was DuckPy, which is, I will say, JavaScript execution environment for Python,

02:32 because one of the problems that I faced was integrating the assets pipeline in web projects,

02:40 and that always requires you to have Node.js involved and Webpack or tools like that.

02:48 And I really wanted to have a solution that worked in the Python environment.

02:53 Like you did pip install, and you got not only all the Python dependencies,

02:59 but also the JavaScript dependencies and your ECMAScript compiled and things like that.

03:06 That's pretty cool.

03:07 So it's kind of like what people are using NPM for on Python web apps to manage Bootstrap

03:13 and AngularJS and stuff along those lines?

03:16 Yeah, it's a suite of various tools, I would say, because it provides a JavaScript interpreter,

03:21 which is used to run things like the TypeScript compiler or the Babel compiler.

03:29 So to translate your JavaScript from more recent versions to the oldest ones,

03:36 you don't need to install Node.js anymore.

03:38 You can use DuckPy and have Python do that for you.

03:42 And of course, it provides a command which is compatible with NPM.

03:49 So you can install NPM packages from Python without having the need to have NPM or Node.js.

03:56 It's not super easy to explain because I think that people frequently get confused

04:03 about its purpose when I try to explain it.

04:05 But the one idea is that you can have, like, run just run your pip install

04:11 and you can have your whole application compiled, JavaScript files compiled back to plain old JavaScript

04:19 and all your dependencies installed, even if they are from JavaScript instead of being from Python and things like that.

04:26 So it makes your life a lot easier because you don't have to care about maintaining two different packaging environments

04:33 and more to different interpreters and things like that.

04:36 I really like it.

04:37 That sounds awesome.

04:38 Cool.

04:38 So let's maybe talk about this idea of the modern standard library.

04:43 So, you know, there's a whole range of people listening who have different levels of experience with Python.

04:49 Let's just start with what is the standard library?

04:52 Standard library is practically everything that ships with Python itself.

04:57 I mean, it's known that Python ships, but it is included.

05:02 And the standard library have those batteries.

05:04 Yeah, exactly.

05:06 They're the batteries that come in the box when you get Python.

05:12 You can get other batteries, but these are the built-in batteries or the ones that come included, right?

05:17 Yeah, I would say that they have far more than batteries because there is tons of things that it can do for you.

05:23 There is a lot inside the standard library.

05:26 And I think it's also something that is not super frequently covered around

05:32 in blog posts or things like that.

05:34 We always tend to have, like, blog posts on the most recent new cool project

05:39 or things like that that is outside of the standard library, of course, because things get included in the standard library after years they are around.

05:48 But that's actually good because it means it's something you can really rely on.

05:53 Absolutely.

05:53 It's important that that has to stay there.

05:55 And there's actually a huge bar for bringing things into the standard library.

05:59 I recall a year or two ago, there was a debate about whether requests should be brought into the standard library

06:05 to more or less supersede the built-in HTTP client capabilities.

06:11 And they decided no, not because they felt requests wasn't good enough, but because requests was changing more quickly than the standard library could really facilitate, right?

06:21 Because it's released with new features really every 18 months and things like that.

06:25 Yeah, absolutely.

06:26 That's one of the problems of the standard library, but also one of the reasons why it's very good.

06:31 Because the things you have there, you can rely on them for the years to come.

06:37 It was especially clear in the recent, in the last years, where Python changed very quick.

06:45 We released many versions of Python in a very quick time.

06:49 And it was clear that even if you are on Python 3.2, and you upgrade to Python 3.7,

06:57 99% of what you use from the standard library is still there, works exactly like before, and you can be fine.

07:05 Of course, between Python 2 and Python 3, there were major changes, but that was expected due to the B.

07:11 That was intentional, right?

07:12 That was like, okay, we finally have to just bite the bullet and make these changes.

07:16 But other than that, you're right, it's really stable.

07:18 There was some blog post or something about somebody wrote how they hated Python,

07:23 and one of their reasons they claimed was, well, if you have 3.5 and you upgrade to 3.6, it might not work.

07:30 I'm like, no, that's actually exactly how it works.

07:34 And I'm really impressed with the stability of Python as it changes.

07:38 I haven't seen any problems.

07:39 The only problems I've encountered is I've used features too new on my dev machine,

07:44 and then I pushed it to production where I didn't realize, oh, yeah, that feature is not yet on my server.

07:50 So that's my own fault.

07:52 Yeah.

07:52 That happened to me from time to time too, of course.

07:56 Yeah.

07:57 Not often, but unfortunate when it does.

08:00 So I think actually when people talk about how amazing Python is, or when they judge any programming language and they compare it against another programming language,

08:11 they might compare the syntax.

08:13 They might say, well, look how much easier like a foreign loop is in this language versus that language,

08:19 or this exception handling block is cleaner than that exception handling block.

08:24 But while that matters, I think actually what most people have in mind when they think about how they feel about a language

08:31 is the standard library of the two languages and maybe the broader ecosystem as well.

08:35 When I think of like, why is Python awesome?

08:38 I don't think, well, because the way the language works with numbers is great.

08:42 I think, well, I can import all these things and solve all these problems right away.

08:47 And that's really the standard library, not the language, right?

08:49 There are many things that are built in Python.

08:51 They're like whenever you use the dictionary or list or things like that.

08:56 They are built into the language itself.

08:59 But every time you do an import, it means you are going to the standard library.

09:05 Unless it's a package you installed explicitly, of course.

09:08 Of course.

09:09 So let's talk about your book.

09:10 And your book is called the Modern Standard Library, Python Modern Standard Library Cookbook, right?

09:18 And I like these cookbook ideas because you're like, well, I'm trying to solve some networking problem.

09:23 Oh, here's two little recipes that I can use to solve that problem.

09:27 So let's just start with what you mean by modern in the standard library cookbook here.

09:34 And then we've selected a handful of specific recipes that we'll talk about that are pretty fun.

09:39 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.

09:50 Because it's, of course, modern because it covers Python 3.

09:55 And most of the things that are there are Python 3 specific.

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

10:04 At the time where I started writing the book, 3.6 was just released.

10:09 So we are now at 3.7.

10:12 And it's probably going to happen pretty soon, 3.8.

10:16 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.

10:30 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.

10:43 So I will say that I tried to think to things that work on Python 3.5.

10:48 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.

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

11:11 So I tried to balance between being modern and covering as many users as possible.

11:17 You know, that's always...

11:19 Yeah, it's a tough balance to strike.

11:21 But I do think the choice of targeting Python 3.5 is pretty good.

11:24 Like, really, if you're using Python 3, most people are on 3.5 or higher at this point.

11:30 And so, I mean, you do give up a few cool things.

11:33 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.

11:39 But at the same time, it's a little more timeless, a little more broad this way.

11:44 All right, well, let's just talk about some of the recipes you have in here.

11:47 So we pulled out some of the more interesting ones.

11:50 You know, what I liked about going through these in your book was a lot of times I'm like,

11:55 oh, I didn't know that class or that function existed and did this.

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

12:05 That's pretty awesome.

12:07 I didn't know that, right?

12:08 So I think these will be pretty interesting to folks.

12:12 And let's just start with that, like reporting errors in production.

12:14 So I guess we should maybe frame this a little bit.

12:18 There's lots of ways to report errors in production or do the other things we're going to talk about

12:22 if you depend on some external library, right?

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

12:31 unless you absolutely have to, right?

12:33 Yeah, absolutely.

12:34 I think that you say that the right thing.

12:37 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,

12:47 or they don't know that they can easily be changed to work in a different way or things like that.

12:54 So like, for example, you mentioned the logging module.

12:57 And we probably all know that it exists.

13:01 And many of us are using it on the daily world to log messages on things like that.

13:07 But the interesting thing is that it has many different handlers.

13:12 So you can send the output of what you are logging in many different places.

13:16 And one of those handlers actually sends the output to an SMTP server, so by mail.

13:23 So you could send your logging messages by mail.

13:28 Right.

13:28 So you've got a logging.handlers.smtphandler class, right?

13:32 Yeah.

13:32 You just plug that in.

13:33 Yeah, it's built in.

13:34 Exactly.

13:35 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.

13:44 But for some specific messages, it might make sense.

13:48 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.

13:57 You can use the logger to only report exception by mail.

14:02 So every time an exception happens in your code, you get notified and you are aware.

14:07 You don't have to wait for the user to come to you and complain that the software is not working.

14:11 You can answer, I already know.

14:13 I've already fixed it.

14:14 Yeah, and that's really cool.

14:15 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?

14:31 Yes, exactly.

14:32 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.

14:46 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.

14:58 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.

15:15 This portion of Talk Python to me is brought to you by Linode.

15:18 Are you looking for hosting that's fast, simple, and incredibly affordable?

15:22 Well, look past that bookstore and check out Linode at talkpython.fm/Linode.

15:27 That's L-I-N-O-D-E.

15:29 Plans start at just $5 a month for a dedicated server with a gig of RAM.

15:34 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.

15:39 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.

15:54 Need a little help with your infrastructure?

15:55 They even offer professional services to help you with architecture, migrations, and more.

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

16:03 Just visit talkpython.fm/Linode.

16:06 It looks like you could probably take this idea and extend it.

16:11 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.

16:23 Instead of sending mail, it logs it remotely to your service, which goes in your database for reporting and whatnot, right?

16:28 Yeah, absolutely.

16:29 You can actually write your own custom handler and log the messages and the exceptions wherever you prefer.

16:36 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.

16:51 Because when you're using the standard library, it's a great way to report your errors.

17:11 So, you might want to switch to a national external service that is dedicated to that.

17:32 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?

17:40 So, you might want to keep it custom for privacy or IP reasons.

17:45 All right.

17:45 So, that's really cool.

17:46 And just to give people a sense, this is like three or four pages in the book.

17:49 It's not a huge, long chapter on it, right?

17:51 These are really quick little things out of many, many recipes.

17:55 So, that's very cool.

17:56 Another one has to do with temporary objects.

18:01 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.

18:10 But those don't behave like objects.

18:13 I can't say container.value.

18:15 I have to say container.bracket value or container.get quote value, things like that, right?

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

18:28 Yeah.

18:29 That's surprisingly one of the most frequent complaints that I hear from, like, new Python users.

18:37 They're still not well-versed in the language.

18:40 Maybe they are approaching Python for the first time.

18:43 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.

18:52 Yeah, probably a C++ and a C Sharp and a Java developer wouldn't have this complaint.

18:57 No, yeah.

18:58 Right?

18:58 Yeah, absolutely.

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

19:05 You can do the same exact thing in Python.

19:07 And there is a fast way to implement an object that allows you to do the same exact thing.

19:15 You can implement your own bunch objects, which are able to store anything in a dictionary.

19:21 Because in the end, it is nearly the same as a standard Python object works.

19:27 Because we store attributes in a dictionary.

19:31 But you can access them to the dot notation.

19:34 So by saying object dot whatever attribute you want.

19:38 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,

19:48 you can even use the arc parse dot namespace class, which does exactly that.

19:54 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.

20:02 But it allows you to accept any property to the dot notation.

20:07 Yeah.

20:08 So in here, you define a class.

20:09 You call it a bunch, which is like just an object that can be extended with arbitrary values.

20:15 And the trick is to have it derived from dictionary, have it implement dunder get attribute and dunder set adder.

20:22 And then it just reads from and writes to the internal dictionary and converts key errors to attribute errors.

20:29 And you have these little anonymous objects you can just create.

20:32 Yes, exactly.

20:33 And one interesting benefit is also that it inherits from a dictionary.

20:38 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.

20:45 Right.

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

20:52 Yeah.

20:52 Which is cool.

20:53 The one trick that I thought was nice, I had seen the get attribute and set adder before.

20:57 But one of the problems is if you go and ask what type it is, you know, type of or type, parentheses, the thing.

21:05 Yeah.

21:05 It'll always say bunch, right?

21:07 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.

21:17 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.

21:32 And you can actually extend that even further to pass type checks.

21:36 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.

21:46 So it can be pretty flexible and convenient.

21:49 Yeah, I really like it.

21:50 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?

21:59 Well, for example, the bunch is something that I use frequently when experimenting, when writing prototypes or things like that.

22:06 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.

22:12 So I frequently end up relying on bunch for that purpose.

22:18 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.

22:28 You don't know what the user is going to need to keep around and things like that.

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

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

22:47 Right.

22:47 Or it's determined at runtime in bizarre ways, right?

22:50 Like, you know, I think of like a CSV file.

22:53 And maybe that's some common stuff.

22:57 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?

23:00 You just gave me a pretty interesting idea, actually.

23:03 It would be easy to extend anything that loads additionally, like a JSON parser, for example, to output bunches.

23:12 So you can access the properties in a normal way instead of having to look up in addition.

23:17 I like it.

23:18 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?

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

23:34 Yeah.

23:34 Cool.

23:35 So one of the next ones that you talked about is templating.

23:40 Now, Python has lots of templating outside the standard library.

23:44 We've got Jinja 2.

23:45 We've got Chameleon templates.

23:47 You can, like, write your own if you really want to go crazy.

23:51 But all those are both external packages with many dependencies and often just found in the web and things like that.

23:57 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.

24:10 Yes, absolutely.

24:11 That's actually one of the recipes that I used for many years.

24:16 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.

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

24:35 But for the previous version of Python, that was not available.

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

24:47 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.

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

25:05 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.

25:20 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.

25:31 And at that point, by subclassing it and driving that method, you can make anything happen.

25:37 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.

25:48 Yeah, that's a really great trick.

25:50 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.

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

26:04 You give it a list of messages and it can basically iterate over them using string.join.

26:10 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.

26:27 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.

26:43 It's really cool.

26:43 I think this is a great example.

26:44 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.

26:53 It can't be great for performance.

26:54 It's not clear really what the output is supposed to be sometimes.

26:58 And this way I think is a lot nicer.

27:00 So quite cool.

27:01 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.

27:15 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.

27:21 Yeah.

27:21 It's just the place is going to be filled in.

27:23 It's really nice.

27:24 Much like a chameleon template or something like that, right?

27:27 A simplified version.

27:28 It's not as powerful.

27:30 Yeah.

27:30 But it's already can satisfy most of the needs that you might have.

27:34 Yeah.

27:34 And, you know, this is one of those cases where I'm like, I had no idea you could do this.

27:38 This is quite cool.

27:39 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?

27:48 And it was, you know, zero to 20, 20 to 40%, 40 to 60, and so on.

27:54 And they copied me thinking, I don't know, maybe they're thinking I would check like, oh, yeah, like 90%.

27:58 But it's because of this stuff.

28:00 I'm thinking like 40% is the right answer.

28:03 Because I know how to do the really common stuff super well.

28:05 But there's all these little extra amazing things that it's like, I didn't even know that existed.

28:10 And so, for example.

28:12 Yeah, I totally agree with you.

28:13 Maybe I'll ask you, what do you think?

28:14 What percentage of the standard library do you think you know?

28:16 And after writing this book, maybe that number went up a lot.

28:18 Oh, yeah, absolutely.

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

28:28 And I wrote a book about it because there are so many things that are hidden inside.

28:33 There's probably not even people that wrote the standard library know every way you can use it, you know?

28:40 Yeah.

28:40 It's pretty incredible.

28:41 Anyway, back to the topics.

28:43 This next one also falls into that, oh, that is so cool.

28:47 I didn't realize this was around for us.

28:50 So I know about working with memory.

28:52 I know about working with files.

28:54 But you have this cool example of I would like to basically cache something in memory or load it into memory.

29:01 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.

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

29:12 So tell us about this.

29:13 So that's one of my favorite tools in the standard library.

29:17 When I need to, like, keep around some data, temporary data, maybe a file that the user uploaded to me or something,

29:26 or something I'm generating, like think of an image you are reciting or things like that.

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

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

29:45 if you are reciting, like, 300 pixels image, it can fit in the memory of every computer.

29:52 But if you are reciting 5 gigabytes of JPEG, it can start to be a big problem.

29:58 And that's the point where having something, a data structure that automatically switches from memory to disk

30:06 on a specified threshold can be very convenient.

30:09 And that's exactly what the temporary file spooled temporary file class does.

30:15 You create one, and at the beginning, everything is in memory up to a point where it grows so big

30:22 that it goes over a threshold that you choose.

30:25 And when the threshold is surpassed, everything switches to disk, and you don't consume memory anymore.

30:30 That is so cool.

30:31 So this is the spooled temporary file, and you just give it a maximum size, and you work with it like a regular file.

30:39 You can write from it, seek on it, read from it.

30:42 And either it stays in memory, or if it turns out that it was got to be too big,

30:47 then it just, you know, writes itself to disk and streams off the disk.

30:50 That's cool.

30:51 Yeah, absolutely.

30:52 Because I frequently saw this pattern implemented by many projects, like use byte.io if the size is small,

31:00 and switch to temporary file if it's big.

31:03 But it can actually be done for you without having to write any code if you use a spooled temporary file.

31:08 This is cool.

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

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

31:21 when it could just be temp file dots, well, temporary file.

31:25 You don't need to implement it.

31:26 It's done, and it's already tested, and it's fast, right?

31:29 I think that's part of knowing the standard library well, right?

31:33 Yeah, and it cannot only make your life easier because it's, of course, probably more tested

31:39 and been around for years and more robust and things like that.

31:43 But it can usually be actually faster because many parts of the standard library are implemented in C,

31:50 so they can be far faster than the code that you've wrote yourself in Python.

31:55 So it's usually a very good idea to look up into the standard library before trying to write something from scratch.

32:03 That touches on this idea of is Python fast or slow?

32:07 And I feel often that the answer is both or either.

32:10 Something like this, right?

32:12 Like you can write code that just runs in pure Python, and it can be very inefficient.

32:17 But as soon as you work with something that just hands off something down to a C layer,

32:22 either that's in the standard library in CPython or if it's in, say, NumPy or something like that,

32:28 like all of a sudden that whole conversation changes.

32:31 And this is a little bit of like you may pick up that advantage automatically

32:35 by just using the standard library stuff better.

32:38 Yeah, I think that it's the only, if not one of the few languages, where the pattern on performance is reversed.

32:46 The farther away you stay from the machine, the faster it goes, you know?

32:51 Yes.

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

32:56 The more you try to work with higher-level data and functions, the faster it will go.

33:01 That's a really good perspective.

33:02 I like it.

33:05 This portion of Talk Python to Me is brought to you by Rollbar.

33:08 Got a question for you.

33:09 Have you been outsourcing your bug discovery to your users?

33:12 Have you been making them send you bug reports?

33:15 You know there's two problems with that.

33:16 You can't discover all the bugs this way.

33:18 And some users don't bother reporting bugs at all.

33:21 They just leave, sometimes forever.

33:23 The best software teams practice proactive error monitoring.

33:27 They detect all the errors in their production apps and services in real time and debug important

33:32 errors in minutes or hours, sometimes before users even notice.

33:35 Teams from companies like Twilio, Instacart, and CircleCI use Rollbar to do this.

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

33:46 in production.

33:46 And Rollbar automatically collects all the relevant data and metadata you need to debug

33:52 the errors so you don't have to sift through logs.

33:53 If you aren't using Rollbar yet, they have a special offer for you, and it's really awesome.

33:57 Sign up and install Rollbar at talkpython.fm/Rollbar, and Rollbar will send you a $100

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

34:10 listed under the Open Source Collective or to the Women Who Code organization.

34:14 Get notified of errors in real time and make a difference in open source.

34:18 Visit talkpython.fm/Rollbar today.

34:20 The next pattern that you talk about is with displaying progress bars.

34:27 And for all the Python developers, you know, think when you type pip install a thing and

34:31 you see the little downloading progress bar going across in the terminal or command prompt,

34:35 like those kinds of progress bars, right, on the terminal.

34:38 That's a very common need.

34:40 And actually, I came up with this recipe because I wanted to showcase a more general need that

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

34:53 environment where you are going to run.

34:55 You know, maybe my user has a terminal which is very small or maybe a full screen or things

35:02 like that.

35:02 So when you try to provide your output, it's always hard to find the right balance of how

35:08 much I should write, how much I should write on a single line, when should I go to a new line,

35:13 and things like that.

35:14 And the progress bar is a perfect example for that because it should always consume as much

35:20 space as available and write the progress on top of that, you know.

35:26 So it was a perfect case to showcase that in the final library, you have tools that allow

35:33 you to inspect the terminal, understand what's the size.

35:36 And we very, like, I don't remember the specific length of the recipe, but I think we are talking

35:42 about, like, less than 10 lines of code.

35:45 You can write a fully functional progress bar that fits and adapts to the size of the screen

35:50 and things like that.

35:51 Yeah, it's a great little example.

35:53 And of course, there are things like TQDM if you want to go outside, right, which is a

35:58 cool progress bar.

35:59 But that's not the same as I have no dependencies and I still have a progress bar, which is pretty

36:04 cool.

36:04 Yeah, exactly.

36:05 Yeah, if you want to cover all the cases, like things like TQDM are very cool when I use

36:11 it in some projects, I think it works great.

36:14 But, you know, one thing is using five lines of code function and the other is bringing

36:20 in a whole library.

36:22 For sure.

36:22 One of the things I liked about this recipe is you used a lot of cool parts of Python.

36:28 So you have a nice decorator that you put onto a function.

36:34 And that function long as it is a generator that returns numbers that can drive the progress

36:41 bar.

36:41 So basically, you do your work and you're looping through it and the function can just periodically

36:47 yield out a number, which is one to 100, which is the progress, right?

36:51 That's a great pattern.

36:53 Yeah, absolutely.

36:53 I think it's very convenient compared to, like, the most common solution that I saw around

36:58 when implementing progress bar is actually to go for a territory that you, like, throw up

37:05 around the original data source.

37:07 And at that point, it computes the site itself, but that requires that you already have all the

37:14 data available.

37:15 Like, I can apply that pattern to a list because I know that a list has five elements, so I can

37:21 compute how much progress I should do for each element.

37:24 But I cannot apply that to a generator because I might not know how much data is going to

37:29 generate a generator.

37:30 And with this pattern is the best balance, in my opinion, because you just yield the progress

37:37 yourself.

37:38 It doesn't matter what you are iterating.

37:39 It can be in the network input.

37:42 It can be on a generator.

37:43 It can be on a list.

37:44 It doesn't really matter.

37:45 It's just the great you're financial with the progress bar decorator and yield the progress

37:50 that you want to report.

37:51 I really like it.

37:52 And it's quite cool.

37:53 So the next one has to do with the overall safety or consistency of a block of code.

38:03 Now, a lot of times people think if they have a try and they have an accept catching block,

38:10 they've handled the exception correctly and everything is fine.

38:15 But that's sometimes true.

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

38:23 have to be made and either all the changes should be applied or none of the changes should

38:27 be applied.

38:27 You know, think of a database transaction, right?

38:29 That's very common.

38:30 Either you get to the end or you commit it or roll it back.

38:32 But that same thing applies in other persistent things.

38:36 Even in memory, actually, if you're changing different parts in the memory of your app, you should

38:40 consider this.

38:41 But this next recipe has to deal with files.

38:44 So if I'm going to be making multiple changes to a file or I want to make sure I write all

38:49 the file or none of the file, how do I do that?

38:52 That's actually a very good question because whenever you handle exceptions, it's obvious that you are

38:58 handling the exception in your own code.

39:01 But it's less obvious that there might be side effects from that exception.

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

39:12 like that.

39:13 What you've wrote so far, it's already there, was already written up to the point where the

39:20 candle flashed the bright and so on.

39:23 So you are not really handling the exception.

39:25 Yeah.

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

39:30 things to a file in the 10th wall.

39:32 Yeah.

39:33 Something is none and I didn't expect it to be and it crashed.

39:36 Right.

39:36 So it sort of just bailed out halfway through.

39:39 Right.

39:39 Which is how do you know that's going to happen?

39:41 How do you recover from it?

39:42 The way you recover is actually not super complex and it can be made a very small tool.

39:49 And in the book, there is this recipe which is called Safe Open.

39:54 So that allows you to open a file in a safe way for writing.

39:59 And whenever you Safe Open, all the writes that you do to the file happen all or nothing.

40:06 So if everything succeeded, all the writes happen.

40:11 If something fails, like you have an exception in the middle between three lines of code that

40:17 draw two pieces, then nothing was written because everything is recovered to the previous

40:23 phase.

40:23 And that's actually done based on...

40:26 I was just going to say, I love it.

40:27 That is such a great thing.

40:28 Like you write, you just write exactly the same code to write to a file, use Safe Open

40:34 instead of Open.

40:35 And then either the file doesn't even exist if there's an error or it's completely consistent

40:40 at the end.

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

40:45 it's pretty straightforward, right?

40:46 Yeah.

40:47 It's like four lines of code or something like that.

40:50 And it actually was like having a database transaction.

40:54 You start the transaction and you can roll back on committees and the middle nothing happened.

40:59 It's super cool.

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

41:06 It is itself a context manager.

41:08 So you put it in a with block.

41:09 If you exit the with block without error, then it will just rename the file to the real one,

41:15 what you're targeting.

41:17 If it exits with an error, it'll just remove the temporary file, which is perfect.

41:22 It's so clean and nice.

41:22 And that works because the rename operation is guaranteed to be atomic.

41:27 So it's all or nothing.

41:29 You replace the old file with the new one or you didn't.

41:32 It's not like when writing that you could have written only part of the output.

41:37 Yeah, it's super.

41:38 Now, this other one is an interesting problem in a more complicated area, but still really good.

41:47 So in Python, we have now, in modern Python, you have basically three ways to do concurrency, right?

41:54 You've got asyncio, which actually uses only one thread and basically releases that thread to do other work

42:04 while you're waiting on like a network or a database or something.

42:06 So that's the asyncio and async and wait keywords.

42:09 Then we have threads and we have multiprocessing.

42:13 And the multiprocessing really exists to get around the limitation of the GIL, the global interpreter lock,

42:18 for computational type stuff, right?

42:21 Mostly.

42:21 Yeah, absolutely.

42:22 That's the only way you can currently work around the GIL and Python because all the other solutions that you mentioned suffer from that problem that you can actually run only one operation at a time,

42:36 which is obvious in the case of async because that's the inner behavior of the feature itself.

42:43 But it's less obvious for new users for threads because they expect to be really parallel.

42:48 And that's the way frequently when talking about parallel operations in Python,

42:55 multiprocessing is suggested because that way you can actually go concurrent for real.

43:02 So the way you get around the GIL, the way multiprocessing works is in your code,

43:06 you say, I want to run these functions with these 10 different values in parallel on different,

43:11 you know, on different basically processes.

43:14 And Python will create 10 sub processes or it can pull it up or whatever.

43:18 But the point is there are multiple other Python sub processes doing the work.

43:23 And then you get the answer back.

43:26 But the challenge can be if I'm doing threading or asyncio, I can just have an object and share it and change it with all the different parts of my code.

43:33 In multiprocessing, that memory is not shared by default.

43:37 So then how do you interact with like if different parts of my multiprocessing thing are generating data

43:44 and they need access to it?

43:45 Like what do I do?

43:46 Yeah, there are many tools in Python for doing that.

43:48 But I think that the most powerful one is the multiprocessing manager.

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

43:59 And even between those that know it exists, even less people know that you have some very cool features.

44:07 Like you can actually replace a whole database system with multiprocessing manager

44:11 because not only it allows you to share the values that you want across the processes that all forked from the same parent.

44:20 So when like using writing a multiprocess program that forks multiple children to do some job.

44:27 But it also allows you to share data across processes that have nothing in common.

44:33 They were started on totally different times, different places, on different machines even.

44:40 Because the way the multiprocessing manager works is that it actually allows you to listen on a port.

44:47 So it works on a plain TCPIP network protocol.

44:51 And you fetch and store values in the multiprocessing manager by picking them to this object and sending them across the network.

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

45:10 It's already there.

45:11 That's a great analogy.

45:12 That's exactly what I was thinking.

45:13 I'm like, this is like a little baby Redis that your process creates, right?

45:18 Yeah, exactly.

45:19 Yeah.

45:20 It's pretty interesting.

45:21 And the big benefit is the shared data can be changed and created over time.

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

45:34 And they can get more tricky, right?

45:36 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.

45:44 It's like basically a little sub database or a key value store that is just live, right?

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

45:57 And I think that it's very cool that it can be easily adapted to work across the network.

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

46:12 Yeah, that's pretty interesting.

46:14 So the next one that we're going to cover is a little bit in this realm.

46:18 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?

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

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

46:39 And they're big, huge dependencies, right?

46:42 They have dependencies of dependencies of dependencies to run.

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

46:54 Yes, absolutely.

46:55 That's actually, in my opinion, one of the reasons why Python has so many web frameworks.

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

47:08 Yes, there's many more.

47:08 We could go for a while.

47:09 Yeah, exactly.

47:10 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?

47:22 And so when you start adding more complex features, of course, it gets far more complex than that.

47:28 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.

47:39 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.

47:46 You can achieve everything you need, including routing, requests, handling, serving responses, and everything.

47:53 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.

48:07 That's pretty easy with sockets and stuff.

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

48:16 Like, much of it is already built in and things like that.

48:20 Yeah, absolutely.

48:20 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.

48:36 So you will not be able to serve more than one request at a time.

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

48:46 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.

48:56 That's really cool.

48:57 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.

49:06 Absolutely.

49:06 And you cover that in this simple little one.

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

49:14 But your very next recipe is, and here's how you serve static files.

49:17 Yeah, absolutely.

49:18 That's very easy.

49:19 So you can actually be extended in just a bunch of lines, of course.

49:24 Quite cool.

49:24 All right.

49:25 So another one that we might want to do in the same general area is creating HTML.

49:32 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,

49:42 basically if you are generating HTML, you need to be really careful about untrusted HTML.

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

49:55 they could put, you know, script, hackable JavaScript or other horrible stuff.

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

50:04 That's one of the most boring parts of writing web application in the past,

50:09 that you have to like to manually go and escape everything yourself.

50:13 I saw, like I still remember those huge files of code where like every single string was wrapped in an escape code.

50:22 And that's horrible because you have to do that for every single thing.

50:26 And if you forget even one, that's a big security issue in your code, you know?

50:32 It is.

50:32 And it's not obvious, right?

50:33 Because like you could have regular text or you could have like Unicode characters that mean the text,

50:40 but they don't look like the text.

50:41 There's just like all sorts of weird ways that people could try to sneak through.

50:45 So you don't want to try to do that yourself.

50:47 That's for sure.

50:48 You don't want to have to care.

50:50 I mean, that's something that I always thought.

50:52 There should be some easy way that does that for me always unless I explicitly want to write some HTML in the output.

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

51:09 And one of the ways you can modify it is actually by doing the escaping of everything you provide to the formatter.

51:17 So whenever you're bidding in your string, all the variables that you inject in the string can be escaped for you.

51:24 So you don't have to care.

51:25 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,

51:33 you don't have to care about properly escaping everything yourself.

51:37 The formatter will do that for you.

51:39 Yeah, I really like it.

51:40 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.

51:51 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.

51:59 Or you're taking user input and that needs to definitely be escaped.

52:02 Yeah.

52:03 It's a cool pattern.

52:04 Yeah, cool.

52:05 All right.

52:05 The very last one that we're going to talk about is tracing code.

52:10 So understanding what code is executed.

52:13 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.

52:24 Like if you want to say, I'm going to run this function, what does it do?

52:27 If it could actually show you the sequential Python that it executed, that'd be kind of neat, right?

52:32 Yes, exactly.

52:32 That's one way I most frequently end up using this recipe, actually.

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

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

52:52 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.

53:05 So you want to follow the flow of the code itself.

53:08 It's not as easy.

53:10 You end up like spending hours just writing next into the PDB to show you where it goes next.

53:17 Yeah.

53:18 Yeah.

53:18 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.

53:36 Hey, I called this method.

53:37 And by calling that method, I also ended up calling all these other methods and they executed these branches.

53:44 And so that's why I got that answer.

53:47 Now I see, now I understand how I ended up having that response.

53:51 Yeah, I think it's great.

53:52 And you could even see, oh, this library is actually calling into this other library.

53:56 Yeah.

53:57 And then maybe even why.

53:59 Why is this a dependency?

54:00 Oh, I see what it's doing.

54:01 It's using it here.

54:02 Yes, exactly.

54:03 You end up discovering a lot.

54:05 And it's like having something that goes through the source code for you.

54:11 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.

54:20 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.

54:27 You do all this work yourself.

54:29 But the tracing module can actually do that for you.

54:32 And so it generates a single flow of everything that happens.

54:36 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?

54:44 What isn't?

54:45 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.

54:51 And it's kind of a little bit of a detective job.

54:55 Whereas this, it only will show you what executed.

54:58 So you can kind of ignore all the other stuff and just see the part that it actually used.

55:03 That's pretty cool.

55:03 Yes, and you just have like to nearly decorate the function that you want to trace, and you will get the output printed.

55:10 So it's very easy to apply.

55:12 That's cool.

55:12 So definitely, we're going to try playing with this as well.

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

55:22 But there's a bunch of other ones, many, many more.

55:24 How many are in the book?

55:25 Do you know?

55:25 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.

55:35 So we are more than 100 for sure.

55:38 Definitely more than 100.

55:39 So there's a lot of these types of little things in here.

55:42 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.

55:49 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.

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

56:01 That was my whole purpose for the whole time I was writing the book.

56:05 That's cool.

56:05 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.

56:11 It's cool.

56:14 It's really great.

56:14 I appreciate the topic.

56:15 So let's leave it there for that.

56:17 But I do have the two final questions for you, and I'm going to change it up just a little bit.

56:21 So if you're going to write some Python code, what editor do you use?

56:24 I usually use PyCharm for most of the big projects editing.

56:30 And for small lacking around, I started using Visual Studio Code recently.

56:35 Yeah, nice.

56:36 That's exactly what I do.

56:38 Yeah, that sounds strange when you tell people.

56:40 They always look at you, Visual Studio.

56:42 No, it's a pretty good editor.

56:44 They are doing a really good job, and they're putting so much energy into the Python space these days.

56:50 So, yeah, I think it's a great answer.

56:52 Yeah.

56:53 All right, that's the editor.

56:53 And like I said, that's basically the same way that I am and using mine.

56:57 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.

57:06 Okay, that's an interesting question.

57:08 There are so many great modules within the standard library that it's really hard to pick one.

57:14 But if I really have to pick a single module, I would say that the logging module is one of the most fascinating ones.

57:22 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.

57:34 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.

57:48 Yeah, that's a great answer.

57:49 I totally agree with you on that, by the way, that just like you could learn it forever.

57:52 You're never done learning it.

57:54 Yeah.

57:55 All right.

57:55 So people are excited about standard library.

57:57 How do they learn more?

57:59 Final call action, what should they do?

58:00 Oh, just go and read the Python source code.

58:03 That's the way that I learned most things.

58:06 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.

58:14 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.

58:30 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.

58:39 Because those more advanced views are not really documented because they are more in internal detail, I would say, of the standard library.

58:49 But once you know that they exist, they've been there for years, and it's pretty safe to leverage them.

58:55 Yeah, that's great.

58:56 I definitely feel like there's a whole bunch of stuff to explore, and even more so after talking with you about this.

59:02 So thanks for being on the show.

59:03 Absolutely.

59:04 Thank you for having me.

59:05 You bet.

59:06 Bye.

59:06 Bye.

59:08 This has been another episode of Talk Python to Me.

59:10 Our guest on this episode was Alessandro Molina, and it's been brought to you by Linode and Rollbar.

59:16 Linode is your go-to hosting for whatever you're building with Python.

59:20 Get four months free at talkpython.fm/linode.

59:24 That's L-I-N-O-D-E.

59:26 Rollbar takes the pain out of errors.

59:28 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.

59:36 Track a ridiculous number of errors for free as Talk Python to Me listeners at talkpython.fm/rollbar.

59:42 Want to level up your Python?

59:44 If you're just getting started, try my Python Jumpstart by Building 10 Apps course.

59:49 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.

59:57 And of course, if you're interested in more than one of these, be sure to check out our Everything Bundle.

01:00:02 It's like a subscription that never expires.

01:00:04 Be sure to subscribe to the show.

01:00:06 Open your favorite podcatcher and search for Python.

01:00:09 We should be right at the top.

01:00:10 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 This is your host, Michael Kennedy.

01:00:21 Thanks so much for listening.

01:00:22 I really appreciate it.

01:00:23 Now get out there and write some Python code.

01:00:26 I really appreciate it.

01:00:46 Thank you.

Back to show page
Talk Python's Mastodon Michael Kennedy's Mastodon