Transcript #271: CPython: Async Task Groups in Python 3.11
Return to episode page view on github00:00 Hey there, thanks for listening. Before we jump into this episode, I just want to remind you
00:03 that this episode is brought to you by us over at Talk Python Training and Brian through his pytest
00:09 book. So if you want to get hands-on and learn something with Python, be sure to consider our
00:14 courses over at Talk Python Training. Visit them via pythonbytes.fm/courses. And if you're
00:21 looking to do testing and get better with pytest, check out Brian's book at pythonbytes.fm slash
00:27 pytest. Enjoy the episode. Hello and welcome to Python Bytes, where we deliver Python news and
00:32 headlines directly to your earbuds. This is episode 271. Really? Wow. Recorded February 16th,
00:39 2022. I'm Brian Okken. Hi, I'm Michael Kennedy. And I'm Steve Dower. Welcome, Steve. So who's
00:46 Steve Dower? Who's Steve Dower? Yeah. Number of things. Probably most interesting to this audience
00:52 is I'm a core developer on CPython, one of our Windows experts. So I spend a lot of my time
00:57 focusing on making Python run better on Windows. I also work at Microsoft, where I also spend a lot
01:04 of my time making Python run better on Windows. So I'm kind of a bit of a one-trick pony, I guess.
01:09 But I feel like it's good work and it helps a lot of people. So if I have a problem with Python on
01:15 Windows, it's your fault. If there's solutions for Python on Windows, then it's my fault. I'll let
01:22 other people own the problems. So if I go to the Windows store, I can now install Python from there.
01:30 And you were part of that, right? Oh, I should have had that up on screen, shouldn't I?
01:34 Yeah, that was actually... The request came from people within Microsoft were like,
01:41 hey, why can't we get Python up on the store? And my response to all of these is like, well,
01:47 if the community is willing to do it, which is half me and is half the people who would have to take over
01:52 if I stopped doing it, then yeah, we'll go ahead and do it. And so I got actual work time for that.
01:58 That was a contribution from Microsoft for that one. But yeah, the community was on board and
02:03 it's going really well. That's also the one that we tied into the default Python.exe that's on every
02:10 Windows machine now. So if you go to a brand new machine and just type in Python, you'll get straight
02:14 to the PSF's Python. Microsoft is not doing it anymore. We just contributed the change. And now I
02:22 switch hat and do it with the other hat on. So it's real Python, right? It's exactly the same as what you
02:27 get from python.org. It's just delivered, you know, easily fast install automatic updates.
02:32 And a couple of edge issues that we're working on bringing down. So yeah, fantastic.
02:40 Automatic update. I know this wasn't one of the topics, but now I think I might have to rethink
02:45 how I'm installing Python on my desktop at work. So that's a cool idea.
02:50 I have only had store installs on my own machines since 3.8. I think I haven't,
02:57 apart from testing, I haven't actually used the regular installer on my own.
03:01 But I mean, you, of course that makes sense, but.
03:03 I mean, you know, it's always testing, right? Every time I'm using Python, I'm testing.
03:08 So.
03:08 Chris May out there says, thank you so much for making my work life in Windows easier.
03:13 Anytime.
03:14 Yeah. Well, Michael, why don't you kick us off with a story or topic?
03:18 I have got a good one. So I'm a big fan of FastAPI and FastAPI being built on Starlet.
03:25 So by the transitive property, I'm also a fan of Starlet. And there's this thing I want to cover
03:30 called FastAPI events. So when a request comes in to a particular API endpoint, or if you convert it
03:38 over to a web app, to a web page sort of request or something, you might want to dispatch that out to
03:44 say like WebSocket listeners or something along those lines. So there's this cool project called FastAPI
03:49 events. It's pretty small and new. So I'm going to try to give it some visibility. It's only got 36 stars.
03:54 It's pretty new, but the idea is that you can go through and basically create this middleware
03:59 handler that will let you say when a request comes in, here's the way when an event is raised,
04:05 here's the thing that's going to handle it. And then in some API endpoint, you can say dispatch,
04:09 give the event a name and some dictionary data to be passed along. I suppose it doesn't have to be
04:15 a dictionary. It could be whatever. And then in other parts of your code and say, I want to just hear
04:19 about this event that happens no matter what API endpoint received it, no matter where in like how
04:26 deep down in the code it was received and so on. So then way down here, you just put a role handler
04:33 decorator on there. You say, I want to capture all the events that start with some substring like cat
04:38 star for like category, whatever, or this one is actually literally about cats. And then you can just go
04:45 through and write these functions that will then handle that. And you can also pass them off to
04:50 queues like you can use the SQS, the simple queuing service from AWS, I believe that is, as the endpoint
04:57 instead of it just being your app. So if you've got like lots of scale out and stuff like that.
05:01 Wow. Of course. So just like a neat way to do logging or even distributed logging, I guess,
05:06 if you've got forwarding handlers in there, you can just...
05:08 Yeah. Yeah. It seems like it, right? Like, or if you want to sort of build up, like, here's the
05:14 request transaction and here we're at this stage, or like you could maybe do like visibility into
05:18 long running workflows with this kind of thing or something along those lines, I would think.
05:21 So yeah, there's also an echo handler for debugging. I kind of like that. Like if I just need to see what
05:27 is happening, it'll just print whatever's happening. It'll just start printing out all the behaviors that
05:31 you're logging. So...
05:32 And then when you want to stop doing that, you just take away the handler and you don't have to
05:36 search the entire code base for print and find everywhere that you added it in for debugging.
05:41 Exactly. Alvaro out there says this looks similar to Django events. Yeah, I suspect. I suspect it is
05:50 similar. Yeah. Anyway, pretty short and simple, but if you're looking for a way to sort of
05:55 put notifications in a structured way into a FastAPI app, well, here you go.
06:01 Oh, I'm thinking of a whole bunch of more abusive ways to use this.
06:04 Yeah, you can write some really impressive spaghetti code with this.
06:11 Yeah, I'm sure that you can. Get the cloud involved in everything.
06:14 Yeah. So let's switch gears a little bit and talk about testing. Imagine that. I've got a testing topic.
06:23 So this is... I'm pretty excited. This is... I've been asked a lot about testing pipelines,
06:30 testing data science stuff, and I'm not... That's not something I do day to day. So I'm really glad
06:35 to find people talking about it. So this... We've got an article from Peter Baumgartner.
06:42 Ways to use testing... Ways I use testing as a data scientist. And I actually just really love this
06:48 article. It's great. To start with, he starts off with what he uses testing for. As a data scientist,
06:56 he uses testing to make sure things work, to document his understanding, and to prevent future
07:03 errors. Well, that seems straightforward. But the reason why he wrote this up is apparently because
07:10 there's a lot of software... There's a lot of testing stuff out on the web, but it's not... It's like geared
07:15 towards test engineers or software developers. And he's like, I'm not a software developer. I'm a...
07:21 And, you know, I'm doing something else. I'm doing analysis. I'm not a software person, even though... Yeah,
07:27 you are. But to write this up in a context where data people might understand it better. For instance,
07:36 he doesn't even start off with writing... Having written tests. His analysis is like, if you're doing
07:43 notebooks or other code, just use a cert a lot. So he's using a cert all over the place, including... He says,
07:52 "Where do you have? Use it for as many intermediate calculations and processes as you can, as it makes
07:59 sense." Because... And doing things like checking obvious stuff. Like he's got an example of a table
08:07 count where he's counting up all the yeses. Well, you can do a little bit of math just to make sure
08:11 the math works. So like all the yeses and nos and missings should all add up to the same count. Go
08:17 ahead and throw an assert in there because sometimes it doesn't. And in this example, he said that he
08:22 actually caught an error because he was looking at two different data frames. So they really weren't,
08:29 they didn't add up to the same. So you can catch things like that. So just double checking yourself on
08:35 things as you go wrong away, go as you're developing. One of the cool quotes he has in here is like,
08:43 as he has a habit of when he's using notebooks to whenever he's visually inspecting the output,
08:50 if you're visually looking at the data that comes out, maybe write an assert statement to do that
08:56 analysis so that it's always checked. And this is a cool use of putting asserts in notebooks. I like this
09:04 idea. The article goes on, it's pretty extensive, talking about checking the data, using hypothesis
09:12 to, well, not the data, not the data at this part, but your assumptions around the data. So using hypothesis
09:20 to check your assumptions and hypothesis will think, show you things that maybe you didn't consider like
09:26 NANDs, are you handling those correctly? Empty series or empty data structures that are going into your
09:32 into your code? Are you handling those? If I mean, hypothesis does have take some handholding,
09:39 but it does make you think about really what is the shape of the data going in? And do you, can you,
09:46 do you need to limit it? What hypothesis is looking at, or do you need to change your code to handle more things?
09:51 Hypothesis is great. I've used that for a couple of, you know, parsing projects or combining projects.
09:58 I spent way too long adding all the strategies to be able to test a URL parser that I was calling into.
10:04 But it's fantastic for finding kind of things that you would not have thought of.
10:11 Yeah. I mean, it's finding things, but it's also, and it does make, yeah, that aspect of it seems like
10:16 the point of it. But the real value I get on a hypothesis is thinking, making sure I really
10:22 understand the data that's going to come in and thinking through those. It goes on to talk about
10:28 actually testing your data using things like Pandera, which I wasn't familiar with, and another
10:33 package called great expectations to look at like putting schemas around the data coming in and making
10:39 sure that the data always matches a schema. Going on to talk about a range act assert and using
10:45 pytest. pytest comes in with, he's only really writing formal tests when he's writing libraries
10:51 for other people. But all these other packages to be able to test with data science, I think this is a
10:57 great addition to the data science community.
10:59 Yeah. Alvaro talks about how this is often referred to as defensive programming. And then,
11:05 you know, I feel for him a little bit, says that work, we use this with our Fortran code. So there's that.
11:11 But I do think this is a really interesting way of thinking about defensive code. You know, I just,
11:16 I think of writing defensive code as like, oh, I'm gonna have a bunch of is statements to verify this
11:20 thing's not none or verify that this is the right type and that it has like a reasonable value and raise
11:25 exceptions. I haven't really thought so much of it for like notebooks. So that's pretty interesting.
11:31 And one of the neat things about like, if you're actually putting a search in your code, you can actually, you can write
11:36 tests against your code that don't even have any certs in them. And because the search will happen
11:43 within your code and the test will still fail and catch it. So it's kind of cool.
11:47 Yeah. Yeah. Very cool. Good stuff.
11:49 Yeah.
11:49 Steve, I am super excited to hear about what you're, you got coming up because this is brand new being a core developer. I feel it is appropriate that you
11:58 break this.
11:58 I mean, I'm not going to lie when it came to, you know, what am I going to talk about? Okay.
12:03 What's the most recently accepted PEP that was somewhat controversial? And I think just as you
12:09 kind of look down to the section on rejected ideas, which is considerably longer than the accepted ideas,
12:16 you can probably get a bit of a sense for just what went on with, with exception groups. And I know,
12:24 Michael, you've just had a conversation, you've learned all about them. So you can, you can take
12:28 over when I run out here, but I'll share my thoughts with it, but yeah, go ahead. I'd love
12:33 to hear about it. This is sort of inspired by trio, right?
12:36 The, the end goal kind of is, so this is an interesting PEP and we've got a few of these on
12:40 the go at the moment. It's kind of like a stepping stone towards a better programming model or a stepping
12:46 stone towards better libraries. So it's, it's something that I think in, in my opinion, very few kind of
12:53 application developers, kind of the, the last developer in the chain is like, I'm often not
12:59 going to use them and they're not going to need them. But as you go further in towards the lower
13:04 levels of libraries, especially people writing async schedulers are going to find incredible value out
13:09 of them. Essentially what the idea is, is that when you're running multiple tasks in parallel,
13:15 if some of them fail, we don't currently have a, have a neat way to capture the exceptions from all
13:21 of the ones that failed. There, there's some approaches that would be like, wait for all of
13:25 them to complete and wrap it in a list. And then you get some exception that contains a list of exceptions,
13:30 but that's lost a whole lot of context. you can get just whichever exception happens first,
13:36 but then you lose all the other exceptions and there's just been no real way to handle it. So an
13:40 exception group essentially does bundle up all the exceptions, internally in some way. But the
13:46 really interesting thing is the except star syntax, which I'm going to have to scroll a long way down
13:51 to find where that comes up. but, but this is really clever because if you're in that situation where
13:59 say you're running 10 parallel processes, so here's kind of the first example of it, then
14:04 exceptions are no longer control flow at this level. Cause if you're, if you've run 10 things and you're
14:11 waiting for 10 things to complete, you're not actually doing control flow with the exceptions anymore.
14:15 What you're doing is handling the exception, but then the control flow is going to go back to where it
14:20 was anyway, because you, because you, you're going to, you know, be doing something different. So for
14:25 example, if a file doesn't open, then you, you would want to, you know, do something different,
14:30 right? You're going to stop going on and trying to read from the file. but if you've tried to open
14:36 10 files and three of them failed, you at the outside level. So at the end, at the inner level
14:41 for each file that may have failed, you'll do something different at the outer level.
14:45 All you're really going to do is say, Hey, this task failed because a file couldn't be opened.
14:50 And maybe you do something else, but it's at the outside level. So except star takes that exception
14:54 group and it's going to give you a chance to handle each exception, essentially on its own.
14:59 It will group them together. So in this example, if you know, five tasks report spam error,
15:04 then you'll get into this except spam error block with all five of them at once. which is just
15:11 what is that a list of spam exception, spam error exceptions, something like, or a tuple, something
15:16 like that. I think it's a tuple. I think with the, the stars in time, I believe, something
15:22 iterable basically. Yeah. Yeah. so something you can iterate over to see the exceptions, but
15:27 it's really just, you know, this happened at some point and you process it. And if the group actually
15:34 contains multiple types of exceptions, then each handler that matches is going to be called for all the
15:39 exceptions that match that. So you could have this tri block raise an exception group that has some spam
15:45 errors. It has some full errors. It has some bar errors and all three except, except star blocks are
15:51 going to get called with the exceptions that match those, which is a bit, it's, it's definitely going
15:56 to confuse a lot of people. It confuses me, which is, you know, why I was keen to actually spend
16:01 a bit more time digging into it, and trying to figure out what's really valuable about this.
16:07 and I do think the most valuable one is really where the error is canceled error, because if for
16:12 whatever reason, five of your tasks have been canceled, then you need to capture that and do
16:17 something with that outside of it. But it doesn't necessarily mean you want to throw away the five
16:22 successful results. And so you do kind of want to, to keep a bit of everything going on. it's,
16:30 and like I say, it's a building block on its own. This isn't enough to do anything new and useful.
16:36 The next thing that comes along is task groups and that's, you know, being worked on by,
16:41 you know, I expect a lot of the same people who worked on exception groups because with task
16:45 groups, now you can actually start, there we go. Guido has just merged task groups. Excellent.
16:50 then now you can actually, like run the task group. And if the group raises any errors,
16:58 then you'll catch them through an exception group. And so that enables a whole lot of, new uses
17:04 and new ways to use asyncio or just async generally, no matter the library, as you say,
17:10 trios already had something like this for a while. and from their nursery thing.
17:15 Yeah. And so that is now being standardized. So libraries can kind of share their implementations
17:21 and work together on it. So one of the reasons you need this is if I start two web requests and
17:28 three database queries, and then I go to wait on them, you know, then the, if, if several of them
17:33 fail, the, the error state captured in totality is a, a tree of errors that represent, well, this,
17:40 this, this, this task started this other task, which then had this error, this other one.
17:45 Right. So you need some way to deal with a, a group of errors that could happen
17:48 kind of all at once. Right. And one of these task groups that gets kicked off.
17:52 Yeah.
17:52 Yeah. So the new task group thing is super cool. So you say async with task group as TG,
17:58 and there's two things that are neat about it. One is right now, if you fire off a bunch of tasks
18:03 and async and await style, they're basically unrelated. Like if one fails, that means nothing
18:08 for the other, right? They're just like, well, here's a bunch of stuff that happened. And this
18:11 creates a relationship between them. Right. So that if one fails, I think it might not schedule new ones,
18:16 something to that. Like it's brand new. I'm just seeing the tweets. So I think that that's the
18:20 story. I believe that was the story of Rio. The other thing that's interesting here that in this
18:25 example, which I all linked to from Yuri that he posted, he tweeted about the news was notice that
18:33 the first one says task group, create task for some task, and then it awaits something that creates
18:38 another task. There's nowhere where you say, store all those values into like some lists of tasks,
18:44 then go to the task and iterate them and wait for them or gather them or whatever the heck it was
18:48 you had to do before. This now makes tasks fire and forget. I can say, run this, run this. And within
18:54 that, I could do more of those types of things. And then you just block at the with context manager
19:00 level to wait for all the tasks to finish, which I think is a real big improvement. Because right now
19:05 you've got to like constantly juggle, well, I've got to return a task from this so I can go wait on it
19:08 later and all those sort of oddities. And this cleans up a lot of that.
19:12 Right. And of course, being Python, I don't know exactly how the syntax works, but being Python,
19:17 that TG object, the task group doesn't actually disappear at the end of the with block. So if
19:21 that's got results stored into it, then you still have access to those and all of the information
19:27 about the task group, even after you've waited for it to complete running.
19:30 Oh yeah, that's cool. Yeah. So I think this is a nice addition to async I/O and Python. This is cool. And apparently 3.11 is coming.
19:37 Yeah, coming in 3.11.
19:38 Yeah.
19:39 I do see a question from Sam Morley in the chat there. Is there a way to short circuit
19:43 so that you don't re-catch certain exceptions? My understanding, and Michael, if you've got a
19:48 better one, correct me, is that the exception, the accept blocks work in the same way as regular ones.
19:55 The first one that matches a particular exception will handle it, and the later ones don't, even if they
20:00 would also match. So if you have, so if the spam error is a subclass of foo error, but there's another
20:07 subclass of foo error, then spam errors will get handled by the spam error handler. The foo
20:13 and foo error handler will handle all the other ones apart from the spam error subclass.
20:18 Nice. Yeah. I don't know much about the accept star other than it was basically a requirement for the
20:23 task group stuff to be implemented properly. So once one came in, then the other could come in. Yeah.
20:28 It's the only feasible way to actually do something as a result of an exception group. Otherwise,
20:34 you do end up with a very generic exception, and then you write a for loop over all the exceptions that
20:42 are handled and try and figure it out yourself. So you'd end up rewriting the code, and it was just
20:47 not going to be feasible. It needed to be syntax. And so it is. Yeah. Right on. Very, very exciting and
20:53 very timely. Thanks, Steve. I'm kind of glad that I put off learning how to do async code until 3.11.
20:58 This looks easier. It's a good band and a good time for async.io. Well, cool. All right. I guess I'm
21:05 up with the next one, huh, Brian? Yeah. Let's see what you got. I have got some other interesting things.
21:10 I'm here about showing off the underappreciated projects or the new projects. Just a couple of stars here.
21:16 And we talked about overloading before, but I thought this was a clean way to do it that people
21:21 can think about. And Steve, I would definitely love to hear your thoughts on this. So Felix the cat
21:25 created this library called pyoverload. And the idea is basically once you have type information,
21:32 then you can have method or function overloading. The idea of being like, okay, I have a function called
21:37 foo or whatever. And you can say, if it takes an integer, I want this implementation to run. If it takes a
21:42 string, I want some other implementation to run. Right. That's sort of the traditional C++,
21:47 C# definition of it. Right. But in Python, we don't have that really because the language started
21:53 without type. So how are you going to figure out the type to overload it? You know, right. That just
21:56 like, doesn't make any sense. So with this one, you could sort of use like traditionally, you could use
22:01 this instance. We're going to do one thing or another. Is it a single thing? Or is it a list of those
22:05 things? What are we going to do? But with this one, you can put just at overload and then whatever the
22:10 signature is, if you can say it has no functions or has no parameters, or it has like two integers,
22:15 or it has three integers, or it has like a list of them, whatever. And there's even a way to sort of
22:21 say somewhere down here, there's a way to say like, if none of them match call this particular one.
22:26 So basically it's, it's just straight function overloading in Python. If that's the thing you want,
22:31 Steve, does this make you cringe or do you like it? Well, you know, I'm, I'm, I'm not going to lie.
22:38 I'm not the, the most into static typing in my Python code as a lot of other people. And, and there's a
22:46 lot of, you know, there's a lot of complicated reasons, but I think for a situation like this,
22:49 I mean, if I know if I was writing a function that took a string or an int, the very first line would be
22:55 converted to whichever type I actually want. And then the rest of the function is going to look identical.
22:59 Yeah. And that's sure. And that, in that case where like, there might be a, an unparse type of thing,
23:05 for sure. I think you wouldn't really do an overload. That would be insane.
23:08 And my, my kind of gut feel, and you know, I'm always open to, to examples proving me wrong.
23:14 in which case I, you know, I would write the is instance code that's in those examples.
23:19 You know, my, my kind of gut feeling is that if you're doing two drastically different things in
23:23 the function based on the type, you need two functions. and once you've got two separate
23:27 functions, you know, if the people calling don't know what they're passing you, then that's, you know,
23:32 they've got a problem and it's not, you know, so much my responsibility to fix it with overloading.
23:37 That said, overloading is really cool. and you know, I am the exact opposite person when it comes
23:43 to like C and C++. I will do all the craziest possible stuff with overloading in those languages,
23:48 because I think it fits the language and it's a lot of fun. and, and there's definitely occasions
23:53 and value for having it in Python. we do have the single dispatch decorator has been part of Python
23:58 for a while, which will do this on the very first parameter. this, you know, very trivially extending
24:05 it to the whole function signature is, is really cool. So, you know, it's, if, if I needed to do this,
24:12 I would probably want to use this, a library like this. would I, would I, you know,
24:17 I'd probably, I, I would probably reconsider my API design choices up to that point.
24:22 but, but I can understand the, the attraction of, of getting to, you know, to, to reuse,
24:28 reuse the name and not make the person calling it think too hard about how, you know, what's actually
24:33 going to run. Yeah. The place where this sort of seems interesting to me is, you know, there's some,
24:39 a lot of like tricks and juggling people do with like star, star, star, star, KWRs where like,
24:43 okay, depending on how you pass it stuff, we'll do a bunch of things. Yeah. And I'm always looking
24:48 for a way to like, not do that. Yeah. How, how can I not, how can I remove that? Like,
24:54 it's completely opaque. I have to do a Google search and read the docs to figure out what is
24:58 at all possible here. Well, one of these days I I'm going, probably going to take all of the kind
25:03 of patents for that kind of thing that I've collected and turn it into a book, but writing
25:07 book just feels like way too much work. So anytime soon. Sorry. my, my colleagues at work can
25:13 ping me at any time and I'll, I'll give them a patent for what they're trying to do. But that's,
25:17 I, I do have quite a set of, oh, you, you're trying to make stuff weirdly work in this way.
25:24 Here's, here's a nice way that you can enable that without having to resort to, you know, type checks and
25:29 everything. Yeah. Yeah. I've been using, I mean, I've been using Python for a long time and I do
25:34 remember one of the first things that I noticed is I couldn't do overloading. And at the time,
25:39 so this was, you know, many years ago, I was using a lot of overloading in my C and C++ code.
25:44 and, and I was like, oh, I can't do overloading. But one of the things I've noticed
25:49 is actually the, instead of keeping wishing that I had overloading in Python, I've noticed that I
25:55 don't really use it in C and C++ anymore. I've, it's gone the other way.
26:00 Yeah. I really, I'd rather be more explicit about the, and just have a function that two functions
26:06 that, that are some, maybe they're similarly named, but they have an appendix that's,
26:12 that's different so that if you have different data, you pass it. And I'm with you, Michael,
26:16 I'd rather have people go, well, which one do I need? I'll look it up. Then just, passing the
26:21 wrong data type and having me, so because, you know, sometimes if they've, if they haven't
26:26 converted the data, like the string string versus number is a scary one for me because I'm often
26:32 getting, getting my numbers from an API or something and they come in as a string. If you forgot to
26:38 convert it and you passed it to the wrong thing and you're really doing something completely different,
26:42 that's, that's not a good thing, but I, I got bit by that one just yesterday,
26:47 updating one of my, one of my CI builds to use Python 3.1, I mean, 3.10. but you know,
26:54 is it, is it a string or is it a number? Interesting. Yes. But yeah, certainly that conversion would be,
27:03 you know, would be worrying. The other one is, is it a string or is it a list of strings? And that's
27:07 the one that bites us in Python all the time. And I don't even know how you resolve an overloaded function
27:13 based on, is it a string or can I iterate it? Well, like in that case, actually, I would rather
27:19 just have that part be part of the function at the, at the top of it, if it can handle both to,
27:24 to check the type and, and iterate or not, but you know. Yeah. Well, all right, let me close this out
27:30 with two quick thoughts. first, I think this is interesting because it's one of the things that's
27:36 possible with modern Python. Like once we've added typing, now you could consider this as a thing,
27:41 whereas previously it really was highly impractical, I think as a way to do, do it. So I think that's
27:48 kind of cool. And then two, I think it might be an, an entryway for people who are not where Brian
27:53 and I'll put myself in there as well yet of going like, actually these things I thought I need,
27:58 I don't need those, right? There's a lot of stuff I thought I needed and I haven't used it for three
28:02 years. So maybe I actually don't need it, but that's not how you maybe first approach,
28:06 approach solving your first problem in Python that you're coming from C++ or whatever,
28:10 C#, whatever. This might be a gateway. So anyway, those are my two thoughts.
28:15 One more thought from Dean after Python 3.11, do we get Python 95?
28:19 I, there, there was, you know, there was a windows 3.12. So I think Python gets to do a 3.12 as well.
28:29 I think it was only available in China. Interesting. And I believe I like to follow
28:34 on with that Dean. Very funny. I believe that Windows 10 was named, you let me know if you
28:41 know different, Steve. Windows 10 was named Windows 10 because there used to be the check Windows 9 as
28:46 the, the starting string for 95 and 98. So you can't be nine because then you're going to be 95. So we got a
28:52 kick on past it. There was some embarrassingly big, language run times out there still doing that
29:00 check. that, that really struggled with Windows 9. and, and showed up in enough places that,
29:07 yeah, I think it just made sense for everyone to just skip it.
29:12 Not skipping 13. We're skipping nine. It's too unlucky. All right. Awesome. Brian, what you got for us?
29:18 Oh, what do I have next? I have, the next generation, Seaborn interface. So Seaborn is a really awesome,
29:27 plotting library built on matplotlib. and I, you know, actually I don't use it that much, but I've always been
29:34 intrigued by it and what kind of watching what plotting libraries do and stuff. And one of the things I was curious about,
29:41 which I'm really grateful for this article, is some of the history behind it. So the,
29:46 the article starts off a next generation Seaborn interface talks about the background and goals.
29:51 but, some of the, some of the great things in here, let me grab, grab some notes.
29:58 this work grew out of a long running effort to refactor Seaborn internals. so that, functions,
30:04 you know, anyway, where I wanted to get at was he was developing a refactor of the internals.
30:10 And he's like, wait, wait a second. If I want to refactor it, maybe I should expose more stuff.
30:14 And some of the background was originally Seaborn was originally conceived of as a toolbox to do,
30:21 of domain specific statistical graphics to be used alongside matplotlib. So the intent was people
30:28 would use both Seaborn and matplotlib together. However, things that people have doing, are doing
30:33 things differently. A lot of people just grab Seaborn by itself. Some people even just learn Seaborn before they
30:39 even learn matplotlib, which is an interesting thing. And that's how I thought you were supposed
30:44 to be doing this, but the concept was, and, and then at over time, there's a whole bunch of features
30:49 that have been added to Seaborn to where it's like really slick looking, but to do the same thing by
30:54 hand in matplotlib is a lot of work. So there's some things that like, if you, if you Seaborn's almost
31:01 there, but you need to tweak it a little bit and you have to do things manually. Well, then you have to
31:05 just do everything by yourself and it's a lot of work. So the idea around this, this, a rewrite
31:10 of the API is let's rework some of the internals so that a lot of the little sub components that go
31:17 inside of a plot are exposed. that way people can get access to it to do a more fine tune
31:24 configuration, within the, so they don't really have to just do everything by hand. It's either all
31:30 or nothing Seaborn or matplotlib, you can kind of do both more easily, which is a kind of a cool idea.
31:35 there's a whole bunch of great details in here that talk about some of the API changes,
31:39 basically exposing the internal. If you create a plot, there's nothing there and it won't show up.
31:45 You have to create layers on the plot. And then within the layers, you've got marks and,
31:50 and different components that go into it. I kind of like this idea of building things up,
31:55 what I really like is the public aspect of this. So you've got a, you've got a library that's out
32:00 in the open. it's being used by a lot of people already. And somebody saying, maybe we should tweak
32:06 the API and do something different and just go ahead and doing that in the open saying, Hey, we're going
32:11 to do this. There's a note at the top, or I'm thinking about doing this note at the top saying
32:15 it's a work in progress. Don't depend on these examples because things might change,
32:20 but this is the direction we're trying to go. trying to get feedback from people. And,
32:25 I think this is a lot of thing, things that a lot of people struggle with when they're maintaining
32:29 packages that have been around for a long time is, I want to do things a little different,
32:34 but am I going to break everybody? and talking through it. So anyway, this is a great read,
32:39 especially if you're a data plotting kind of person.
32:43 Yeah. Very nice. I always want to do more with visualization and I'm sure that I have some good
32:47 data I could pull up. Yeah. I ended up basically just writing APIs and websites these days,
32:52 but, but I really should be pulling this up and doing some of these graphs and I'm really happy
32:56 these are, these are around. Steve, how about you?
32:58 See, Seaborn's great. It's always, like back when I first discovered it, one of its major
33:04 selling points was simply importing Seaborn would magically make your default map plot.lib charts look
33:10 nicer. which, which map.lib is.
33:13 I love it. It's like the bootstrap of map plot.lib.
33:15 It really was. It's like they, they just apply their style by default and every map plot.lib chart
33:21 suddenly looked nicer, which, you know, map plot.lib's done their own styling work now. So it's
33:26 less valuable for that. I, I do like this API. It looks good. And, and as Dean's pointing out in
33:32 chat, it's like map plot.lib has an object oriented plotting API similar to this, possibly identical,
33:37 just like everyone else. I've never learned the object oriented API. but, but it is there.
33:43 And it's, it's, you know, it's, that's the modern one. It's like, I know a lot of people say map
33:48 plot.lib is impenetrable, and kind of hard to build things up, but it does have a really nice API
33:53 there. It's just not the, the pipe plot one that kind of imitates mat labs old API. and, and so,
34:01 you know, having it there is, is really nice. And Seaborn, you know, having their own is also great.
34:06 another, nice, thing that to read about in this is, he's does a hat tip to a
34:14 ggplot or ggplot two or whatever it's called. saying that, yes, it's going to look, a lot of
34:21 this is similar to ggplot, but, it isn't that I'm trying to copy it or maybe that's, that's definitely
34:28 influence, but, it is, that Seaborn is, is important because we think about things
34:35 differently in Python than we do in R and, and, and just having, it would be, but also a hat tip to
34:42 another library that is a, a wrapper around ggplot. If you just want that, you can do that in Python too.
34:48 That's available. So, I, it is interesting to, these are, we think of these as competing libraries,
34:54 but they're really not competing with each other. They're working together to push the, push
34:58 plotting forward. So, yeah. Nice. Dean out there points out you can do plot.style.useSeaborn or ggplot,
35:06 which is another one. Let me throw out. Oh yeah, go ahead, Steve.
35:10 ggplot's certainly the one to copy from. I mean, that's the, there's a reason that one is, you know,
35:15 as universally popular as, you know, any plotting library can possibly be. it's probably even,
35:22 you know, it's probably competing with Excel for popularity of plotting data. Realistically, it's,
35:27 it's, it's a really nice API and it looks good and everyone's familiar with it. And so, you know,
35:32 there's nothing wrong with copying from ggplot. Nice. I got one more shout out to throw,
35:37 into this conversation, the XKCD plotting style for, matplotlib. So you've got, I mean, this is
35:46 fantastic. It looks like the, it really does look like XKCD would, you know, the comic would do
35:54 for, for these. So, this is fantastic. I love it. What I love is I actually see this. I see this
35:59 in papers and stuff like that. People just go ahead and use the XKCD style and for serious stuff. And it
36:05 just is, it's awesome. I love it. I think there's actually some value to having like cartoony looking
36:10 graphics, like UI sketches and graphs to say like, look, this is speculative. This is just like,
36:15 don't read too much into it. I'm trying to give you an idea rather than an exact thing. And I think
36:20 sort of a UI like cartoony looking sketches. And this also plays into that. Yeah. Right. Steve,
36:26 you got the last one. I got the last one. Yeah. So this is another kind of recent, delivery from,
36:31 from the CPython core team. we can now compile CPython to WebAssembly. so, and to a lot of
36:39 people that probably means very little, but I guess the brief, brief summary is,
36:43 is WebAssembly is, kind of what the JavaScript in your browser compiles to before it runs.
36:50 So it's skipped that initial step of being JavaScript and it's now ready to run in the browser.
36:56 so it's, it's a lower level, there are tool chains out there that can compile all sorts of languages
37:01 directly to WebAssembly. and so in this case we've taken, I believe we use one of the,
37:08 I don't know the exact tool chain that's used and it may not matter, but it basically takes the C code
37:13 and compiles that to WebAssembly gives you a package that can be brought into an electron app
37:19 or a node JS app or a web browser, modern web browser and be run in the browser. there is,
37:25 so this page is a little bit dated. there's been a bit more work since then, but I found this is the
37:30 best overview of where things are kind of at long list of C extensions that don't work. Probably unsurprising,
37:35 like the browser doesn't have a lot of this stuff in it. Yeah. You don't have,
37:40 what no TK, all the different APIs, the win 32 API underneath or whatever it was delegating to you.
37:45 Yeah. Don't no Tkinter. What? No. Yeah. No, no Tkinter, no sub process, C types. Apparently
37:53 you can do, I've, I've heard there is a lib FFI port to, to the M script and kind of platform.
38:00 so how, how, how this kind of works is kind of when you take WebAssembly in, into a browser,
38:07 it has access to nothing. Like it starts off in a really enclosed kind of box of things that it can
38:12 do. and, and that doesn't actually work for Python at all because the very early thing that
38:17 tries to do is search the file system for what files it should be loading. so, so you, we actually
38:23 build it as part of another platform. And script is one platform that kind of poly fills a whole lot of
38:31 native looking APIs so that code that's compiled on top of them script and is able to use it. And that
38:37 this little demo, which I just hit start REPL, this is running on that. So this is a build of
38:43 Python 3 11 alpha four built with clang running on em script. And I can, I believe I can do this
38:51 and be like OS Lister. And, and it thinks there's a file system there. Now that's not my file system.
38:56 That's in memory. it can be changed to browser storage. but this is entirely in the
39:01 browser. Like there's nothing downloaded. There's nothing running on my machine here.
39:04 There's nothing running in the cloud. it's literally in the browser. I can probably freeze
39:08 my browser with this. Like I can do an infinite loop and do it, do it. Let's see if this cuts me off.
39:13 I'll just let that run. What happens if you hit clear?
39:18 Started again. We're going to start again. No, no, no, no. It's done. I'll just refresh. And yeah.
39:26 so yeah. And, and the, there's a second one that the actual builders, it was committed supports,
39:32 which is WASI W-A-S-I. that's a slightly different approach to adding all the functionality
39:38 around a WebAssembly module. it's, so it's a little bit more flexible, a little bit more controlled.
39:44 And scripted is really like, give me POSIX system inside my browser, all in memory. and so, and so
39:51 we have two options, and these are available in the, in the main branch. I don't, at the moment,
39:58 we're not shipping prebuilt modules for WebAssembly. That might be a possibility. if that's something
40:03 that you'd like to see, then I guess go to discuss.python.org and post about it. It's probably
40:08 a post there. I should have looked for a post there. but we're not currently doing prebuilt releases,
40:14 but, but I think we could, I think this is one of these options where the, the WebAssembly build is
40:20 totally portable. And so if we build it, we can distribute it. And then, you know, websites that
40:25 want to do something like this could just download it from our servers and, and run it. So I think
40:30 there's, there's a lot of potential here. what I'm, you know, and, and, and it's at the potential
40:36 stage, right? This is another stepping stone to bigger and better things. our kind of responsibility
40:42 as the core team is to enable it. And now we really want people to come in and pick this up and do
40:46 awesome things with it. Firstly, so we can figure out what gaps still need to be filled. but also just,
40:51 just to, just to expand, you know, the growth and the reach of Python, to bring it into places
40:55 that currently doesn't exist or can't work, and you know, give it new life and new places,
41:01 open it up to new people. This is fantastic. Congratulations. and so the work for this
41:07 primarily done by Katie Bell, Christian Himes, and Ethan Smith. So I think Christian got to do all the
41:14 merge commits, but it's definitely been number of people working on this for a while. those are the,
41:19 the, the primary three. I'm really excited for the possibility for this. I think one of the
41:25 things that could be amazing, obviously running it in the front end is a thing that could be done.
41:30 I saw the documentation said it was about 10 megs to download it. I'm sure you can put that on like a
41:36 CDN. So you kind of hit it once somewhere for a particular version of Python. That's pretty good.
41:41 You know, it's, we all have pretty fast things these days. Yeah. It's still bigger than doing it.
41:46 Yeah. What gets me really excited though, is, putting that into an Electron JS app.
41:52 Yeah, absolutely. Right. Because Electron JS is a really interesting way to bring web technologies
41:58 cross platform as much as I, I like, oh, I said an Electron app. Still it's, it's really opened up the
42:04 possibility for a lot of things, but it really has meant, okay, you're doing TypeScript, you're doing
42:08 JavaScript and you just have to go full on in that world. So here you could still do like your front
42:13 end and whatever, but having the core logic of that desktop app being in Python running in this,
42:19 that's exciting. If that's, that can be put together.
42:21 I should also add two things. Pyodide is a project that people have probably heard of before,
42:27 which has been working on this for considerably longer than the core team has. And so I think a
42:31 lot of the patches that needed to happen have come from them and they now get to spend more time focusing
42:37 on the data science stack, which, cause they've got ports of NumPy and pandas and other libraries,
42:43 to actually do data science in the browser. and the other interesting thing that I saw was
42:49 someone from, from CondorForge suggesting that they could elevate was wasn't builds to
42:54 their kind of automated level. And so all of CondorForge may suddenly become available to use
43:00 in the browser on top of a build of Python like this.
43:03 Wow. So that, that would unlock so much. That would be incredible.
43:07 Yeah. Interesting.
43:08 I imagine initially it would unlock a lot of bug reports, but, but we need to work through those first.
43:14 Yeah. I was just thinking of, you know, take the top 1000 most popular packages,
43:19 you know, could you get 90% of those compiled to like other WebAssembly things that then could be
43:24 included and then imported here somehow.
43:26 Exactly. And the top 1000 with native code, cause it's only the native code, right? The Python code
43:32 still compiles in the browser, just like it would in the CPython interpreter.
43:37 it's only the native code that has to be ported and built. And so once that's done, then,
43:42 you know, grow up and running. So the top 1000 is probably more than you need.
43:46 Yeah, absolutely. All right. Awesome. I'm looking forward to seeing where this goes.
43:50 So many neat options. There's, there's just cool ways to say, like ship the Python runtime
43:56 to places where maybe it would have been hard to get. Now you drop this WASM file plus something that
44:01 can run WASM. And then now you've got a deployable shipable. Yeah.
44:05 See Python runtime without Tkinter and a few things, but still you might not miss it.
44:09 It depends what you're doing. I mean, most apps are not Tkinter apps is all I'm saying.
44:14 I'm not trying to bang on it.
44:16 No, no, but I just haven't. Every time it comes up that it's still there. I'm like,
44:20 really? We still have that. Okay.
44:22 Don't ask me what I've been spending my week working on, Brian. It's not going to make you happy.
44:28 Are you, are you creating a Tkinter base killer for against textual?
44:33 No, no, unfortunately not.
44:37 Awesome. All right. Well, Brian, are we at extras?
44:43 We are at extras. Do you want to kick us off?
44:46 I will kick us off. So I've got a couple of things that I think are interesting. Let's
44:51 start with this one. So we've talked about, oh, oh, my Z shell, right? Yeah.
44:57 A lot. Love it. I just came across, realizing that actually this is a Portland company that puts
45:03 together the sort of core maintainers of, that. So I just thought it was funny to give a quick
45:09 shout out to, planet Argonne. They're not really in the Python space, but they're in Portland,
45:14 which I thought was kind of fun. and then what is this? This, next one comes to us, I think
45:20 via PyCoders. That's where I got this. Django just reformatted all of Django with black. And I know
45:28 I know I was just having a discussion with somebody like, oh, your code doesn't have,
45:31 doesn't follow PEP 8. Oh, like, oh, I don't want it to follow PEP 8. Yeah. But if people are
45:35 going to use your code, you know, like literally you got to import it, then it probably should follow,
45:39 like it should not come up with all sorts of warnings. And so I thought it was interesting that
45:43 Django just said everything, make it black. Steve, what do you think about that?
45:47 I'm totally on board with, with just using black on everything. I don't agree a hundred percent with
45:53 the style, but I agree a hundred percent with not arguing about it. So yeah, it's close enough.
46:00 Yeah. Plus, there's enough tweaks like that make it good. Like I really, I'm really grateful
46:06 that that has, you can tweak the line length for instance. Yes. Because I mean, here's example.
46:12 What if I want it really short? So, for no, for seriously, for formatting the code for the,
46:18 the pie test book, I wanted them all quite a bit shorter so that they fit better in a book format.
46:24 And I could use black to cover with that and convert everything with, with black to make them like that.
46:31 So it was great. Nice. Awesome. All right. And the final one is I have been doing some stuff with,
46:37 more fun things on YouTube, trying to put these little short videos together. So here's
46:41 a, how long are the six minutes, 44 second video on using time Delta to get like,
46:47 how many weeks are in some time span that, you know, the cool tricks you can do there.
46:51 So people should check that out. That's my latest Python short thing. And yeah, that's,
46:55 that's it for my extras. okay. So I've got a quick one. Just, I've got, I don't have a graph, something to throw up, but I just, I was looking at looking at the get history of,
47:06 of, a repo and trying to figure out whether I included one of my coworkers,
47:11 branches in it, if I merged it yet or not, things like that. And I was on the command line
47:16 and I just learned, I'm like, can I just do this with the command line? Apparently I didn't know this
47:21 exists. So apparently a get log --graph just shows you the, the get graph that your branch
47:28 history or the branch graph on the command line. And I didn't know it was there until today.
47:34 I started using it, tweeted about it. And then a whole bunch of people said,
47:38 Oh, you should use these flags too. That makes it even nicer. So, so it's fun to,
47:43 to learn something old as a new thing. And then somebody else told me, how about get K? So,
47:51 get K is a, is a graphical browser of your repository that just comes with most
47:58 get installs that I didn't know was there. I'm like, do I need to install it? I'll just type it and see what
48:03 happens. And it popped up this graphical interface. I'm like, this is great. This is exactly what I
48:08 wanted. So get K is pretty cool. I didn't know about that one. I I've seen the command. I've never
48:13 actually run it to see what happens. So I was not feeling quite brave enough.
48:18 Did it mean get kill or was it something else?
48:22 I was just most get commands scare me until I've run them the first hundred times or so.
48:29 How about you? Yeah, I got, I got a couple of extras. Okay. Can I get my screen back up there?
48:32 there's, I was feeling a little bad about, you know, being a bit self-serving here,
48:36 but then Michael just promoted his video series. So I don't feel too bad anymore.
48:40 Get it. Get it on, man.
48:41 This is the, the Python 3.11 alpha five download page. And we have a new addition this time around,
48:46 which is this windows installer for arm 64. So arm 64 is not a massive, massive platform for windows yet,
48:53 but it's growing and, we want to have Python support on it. So the builds have been running in
48:59 the background for a while, but we've never actually released it. We're hoping to get it out with 3.11.
49:03 That is going to depend largely on, do people use it? Do they love it? Do they hate it?
49:09 My experience so far with it has been that it is noticeably faster, on at least on the arm 64
49:16 devices I've had access to compared to the Intel devices, which is really, really cool. and
49:22 there's, there's like the test suite is, kind of 30 to 50% faster, which is huge, huge really. So,
49:30 so I think there's a lot of potential here. I may just have had awesome hardware. I'm not sure it was
49:34 a virtual machine, so it's kind of hard to tell. Yeah. but this is fantastic. This is new.
49:39 if you have an arm 64 device, like a surface pro X, or there's a couple out there from other
49:44 manufacturers, I'm running windows 11 arm on my MacBook pro and through parallels.
49:50 I then use please in download and install it and let, let me know how it goes. If you get it
49:56 through the windows store, which is currently still not public, you need to get the link from basically
50:01 from one of my tweets, to the windows store, you'll automatically get the arm 64 version on arm 64
50:06 as well. So this installer is the traditional one. otherwise you get it through the store.
50:12 The other thing, which I wasn't going to do, and then I spent a bit of time working on this,
50:16 a couple, couple of years back at the, at the core dev sprints, I forget who I was chatting
50:22 with. I was chatting with one of the other core devs about everyone typing from collections, import deck,
50:28 and misspelling it. And it's like, you tell, you know, so a deck D Q U E is double ended Q,
50:34 very useful data type for certain purposes, but people would type it deck, like deck as in D E C K.
50:39 Because it's phonetically what it sounds like.
50:41 Exactly. And so as, as a bit of a joke, I made a package that when you, when you installed it,
50:47 it would give you from collections, import deck. and obviously the thing that that collection
50:52 should be is a, a deck, a double ended Q of 52 cards representing the cards in a normal deck.
50:58 and over time for various reasons, it's just kind of grown. And, and I recently, you know,
51:03 added support for calculating poker hands to it. And so now you could build a game with this.
51:08 it does, it uses enums. It's got shuffling, dealing, jokers are optional. and you can
51:16 calculate a poker hand and yeah, my, my little compare them poker hand one greater than poker hand two.
51:22 I, I spent a lot of time. most of my work on this over the last week was writing the tests
51:27 that proved how incorrect that function was until I wrote the tests for it. but now at this point,
51:33 yeah, it's, yeah, you can look at the values it gives back. It's actually a tuple with an enumeration
51:38 saying what the hand is and then a selection of the card values in a way that makes the tuples
51:43 comparable. So you can actually look and see, you know, it's a pair of, of aces that will have the
51:49 number 14 there for the ace. And the next highest card was a 10. So if someone else has a pair of aces
51:55 and their next highest card was a nine, then you're still going to compare higher. So I'm pretty proud
51:59 of that function. Yeah, that's clever. But, but yeah, this is, and, and,
52:03 and it's code style black. Nice. Oh, very nice. So yeah, it's, it's one short file. and it does
52:10 still override deck in the collections module for you. So I love it. It doesn't over. So, so that deck
52:18 isn't there, right? It's like DQ is, is untouched, but if you try and import DCK from collections,
52:24 then you'll get it. Nice. Hey, one of the quick things to shout out, we're hiring contractors
52:30 to help develop features for pypi.org. It says at the top of pypi.org. That's, do you know anything
52:35 about this? I guess if people want to work on pypi.org, that's pretty neat. Yeah, no, they, they have
52:40 funding and, there, there is a post that describes the surveys. I believe this is the organizational
52:46 accounts project they're looking at. Yeah. Organization accounts. So, if, if like me,
52:54 you are kind of the, one of the primary Python people at your company, then you'll spend a lot
52:59 of time helping people publish packages to pypi. If that's the business you're in certainly is for,
53:04 for us, there's a lot of packages from Microsoft up on pypi. and the, the, the kind of corporate
53:10 account for that is, but it does exist. We have a user account with 483 projects. This is all manually
53:17 curated right now. cause pypi just doesn't have the functionality to kind of hand out permissions
53:23 to it safely. The teams and all that kind of stuff. Yeah. Yeah. So, so I believe the, the idea of this
53:30 is to add that functionality to pypi. So I would love it if someone comes along and does this. I, I believe
53:36 we've contributed some of the funding towards this. So, you know, yeah, it looks like it.
53:40 Okay. Oh, so Steve, I've got a 3.11 question for you. So, 3.11 is an alpha. So what does,
53:47 what does that mean really? does that mean I can like start using 3.11 or should I wait?
53:52 it, it means you can, it means we still may change stuff that will break you and we won't
53:58 apologize. Okay. But if my code runs, can I trust it or? Yeah. If, if you're, if all of your tests pass,
54:07 then you should be, you should be out of trust it fairly well, certainly existing code.
54:11 there will be new features available in the alpha that have not been as thoroughly tested yet or may
54:17 change again. But again, if you're running existing code, you won't be using those. So, so that won't
54:22 matter, but yeah, it's totally viable to use. You can specify 3.11 dev on GitHub actions. I believe it
54:28 compiles from source when you do that. Now, they don't have a build there. They should for beta.
54:34 beta is when we really want people to start doing stuff at this point. alpha is so that
54:40 people can test the new features kind of targeted testing on anything new that we've put in beta is
54:45 when we really want people to start, porting libraries, especially kind of the core libraries to
54:51 be able to work with it. and, and just test it because if existing code doesn't work on the beta,
54:57 we want to hear about that so we can fix it in the runtime and not force you to fix it in your code.
55:02 Okay. But if I'm like a package maintainer, I can start, if it's got GitHub actions for it,
55:06 I can start testing, having my CI test against 3.11 then two.
55:09 Absolutely. Okay. You will likely want to market as it's okay if it fails.
55:14 Okay. Yeah. Awesome. Okay. Thanks. should we do a joke?
55:19 Let's do a joke. Let's, let's do a joke. All right. So this one coming from the programming humor
55:25 one and it's a, like you talked about the visualization stuff, right? And this one,
55:30 it says there's a search that says how to get labels on MATLAB bar charts to be horizontal.
55:36 Well, look what the results came back from Google was says you're not alone. Help is available. If
55:42 you're experiencing difficult thoughts, please call one one six dash one two three, or if you,
55:47 this is an emergency called nine nine nine. And the underlying bit here is it isn't that drastic
55:54 Google, but thanks. And I, I believe it might also work on being, I'm not sure.
55:59 There's a few scrolling on. I think there's a being equivalent down here. Yeah. Not just Google
56:05 being thinks you're an emergency as well. yeah, that's awesome. Yeah. So it's not that,
56:13 that, that much of an emergency. I'll go to stack over for it.
56:16 Nice to know that the big search engines are looking out for our mental health.
56:20 That's right. People become very upset after failing to get those bar charts.
56:27 This is not the emergency, but it's coming next when you realize what the answer is.
56:30 It's something that I don't know.
56:32 Nice. All right.
56:34 Anyway, that's the joke I found for us guys.
56:35 Well, thanks everybody. Thanks, Steve for coming and thanks Michael again.
56:40 Yeah. Thanks for having me.
56:41 Thanks all. Bye. Bye.
56:42 Bye.
56:42 Bye, everyone.
56:43 Thanks for listening to Python bytes.
56:45 Follow the show on Twitter via at Python bytes.
56:48 That's Python bytes as in B Y T E S.
56:51 Get the full show notes over at Python bytes.fm.
56:54 If you have a news item we should cover, just visit Python bytes.fm and click submit in the nav bar.
56:59 We're always on the lookout for sharing something cool.
57:01 If you want to join us for the live recording, just visit the website and click live stream to
57:06 get notified of when our next episode goes live. That's usually happening at noon Pacific on Wednesdays
57:12 over at YouTube on behalf of myself and Brian Okken. This is Michael Kennedy.
57:16 Thank you for listening and sharing this podcast with your friends and colleagues.