« Return to show page
Transcript for Episode #221:
Pattern matching and accepting change in Python with Brett Cannon
00:00 Hello, and welcome to Python bytes where we deliver Python news and headlines directly to your ear buds. This is Episode 221. I think recorded. When is it February 17 2021. I'm Brian Aachen.
00:12 I'm Michael Kennedy.
00:14 And we also have Brett cannon. Yeah. I always talk over the third person. Not sure why.
00:19 Well, you're always going like, Is there gonna be an introduction? Or should I just joined the Brady Bunch theme? Let's just go in a circle and just introduce ourselves.
00:26 But we're going to do Brady Bunch, we got to have more people. So we can like fill out what does that like rows of three or whatever in the in the picture?
00:33 I'm sure Anthony Sean Paul averred available based on how often they show up here. So you're waiting in the wings?
00:38 That's right. We'll just swooped in here for the right picture. Well, welcome, Brad. It's great to have you here.
00:43 Lovely to be back.
00:44 You know what, Brian, I'm impressed with Brett's microphone, he is mastered the work from home. Yeah,
00:50 I love this, we're gonna have to put a link in the show notes to this thing.
00:54 tell people about that. So Brian and I both have good microphones. We have many different microphones we've tried. But yours has a visual aspect, which is unusual for audio. Yeah, so
01:03 it's a hyper x quad cast s specifically. And it comes with RGB lighting, because it's really a gamer mic. But I've seen in pandemic times I work from home. And my wife is also at home because of the pandemic. And we always had an issue of her wanting to come in and not knowing whether I was actually occupied to actually in a meeting because I'm in them a lot for work or whether or not I was just sitting there with my headphones on listening to music or whatever. And she could totally just walk in. And so the great thing about this microphone is if it has a touch sensitive top, and if you tap it, the LEDs turn off. And so it acts as like an on air light for the room. I mean, it's not that bright specifically, but if she opens the door, she can totally just look in and go, is the mic lit? And if the mics not lit, she knows she's not going to be heard or be directly interrupting me and I can just turn and answer any questions he has. And it's awesome. It works out great that way
01:55 that is so fantastic. The whole mic glows when it's on an act of different colors. It's beautiful. I love it.
02:01 Yeah, and you can even change colors it uses this is camera if it's waterfall or not, you can actually change how the color spectrum changes from side to side in terms of colors. And whether it's top to bottom or 90. It's very pretty. I think I once did Harry Potter color themed for a coworker of mine is a big Harry Potter fan for all the houses and I've purposely not played with it and kept with the defaults because it's definitely one of those things I could totally nerd out on and play with the colors all day long. I tried Python colors once the transitions didn't look quite as nice, but
02:29 yeah, no, it's good. I have the microphone that matches or sorry, the mouse that matches it. Oh, nice. It pulses so I didn't get it for the color but it's the only one I could get that had color like Why does it have color? How do I turn this off? But now it just has colors fine. But anyway, great. Mike I'm I think people should check that out. If they have like weird work from home situations and you people are interrupting it's super cool. I love it.
02:49 definitely get a mic. mic stand though the default stands a little low. And I found that the getting a cheap, like 10 $15 stand I got this from Amazon that pushes it up about seven inches. I think trying to do the American units pushes up about 15 centimeters is a nice sweet spot in terms of distance while still not completely taking up your vision in the screen.
03:11 Yeah. Excellent. Excellent. Well, Brian, you want to kick us off here with our first topic?
03:16 Yeah, well, we've got it's hard to keep up with Milton will mC mC couldn't make it sorry. Well, we'll and broke rich, which is a free what he calls it basically, it's a CI tool. But there's been a whole bunch of really cool, cool things he's done recently, like, really recently. So in December, he added Live View, which allows you to just it continually updates whenever an event happens or something in that part of the screen, which is cool. He didn't write a blog post on that. But then in January, tree view came out and the tree views are lovely. You can do all sorts of stuff with the tree view. And yeah, just it's it's a nice thing to have in a in a CLA tool. And then what's really impressive to me, I think was layouts. So this month, he released layouts, and they it allows you to split the screen up and is his demo. Even shows it got happening, like within a VS code. When turbo with Oh yeah.
04:24 Within the bottom. Yeah, terminal part of VS code. Yeah. Brett, you've heard of this, right? You've heard of this tool?
04:31 How can I not? I'm a fan of the podcast and Brian loves this tool multiple times.
04:37 So I guess we've covered it before. But one of the reasons why I'm covering it again, there's a couple of reasons. Is the layouts neat? And really, now I want to click on stuff and I want to do entry and any sort of hinted at that that he's kind of going towards a full to E which I had never heard of before text user interface.
04:57 Yeah, two E's. All right,
04:58 right. That'd be cool. So I think that'd be neat. And I'd like to encourage that. So the other thing that that's going on with rich is he's, he's accepting sponsorships now. And it's kind of hidden in the demo window. But what we're highlighting it on the show, so more people can know about it. So, GitHub is doing sponsorships on GitHub, and I like how GitHub sponsorships work. It's kind of neat. So check it out. The other thing that I guess when I was playing with these layout, things that I didn't really realize before is you can just say Python dash m rich, and it shows a little demo of a whole bunch of stuff. And then some of the particular parts of it, you can demo them individually. Like for the layout, you can say Python dash m rich dot layout, and it'll show a demo. The the tree is also as a demo and the live one actually. I couldn't escape it, I had to kill my windows. So the live one just continually updates some some, like some, I think it does. I don't know if it's actually worth doing going out to the internet. But it's, it's doing some like conversions of monetary conversions. And it just kept updating and I, it doesn't hit Ctrl C. So
06:12 a bear except somewhere they shouldn't have.
06:16 I think it's very reliable. I mean, the only way to execute is force quit. So we're all friends.
06:25 So yeah, I'd love to have some have rich continue with this and make it a full to eat. That'd be fun.
06:31 That would be very cool. Because the presentation is really nice here.
06:34 I will also fully support will making this a full on thing because actually a couple months ago, I did a slight dive on a weekend trying to find some UI libraries. Because I have a personal project for work purposes that I've been trying to do a GUI for in two years in a row. I've tried to do it during hackathon week at Microsoft and I keep failing because I don't have enough time to dive into all the GUI libraries that go from like, here's how to do Hello, world. Here's the reference. And it's like, No, no, I need more more steps there.
07:05 Where's that middle ground? Come
07:06 on? Yeah, exactly. And then I decide Oh, well, this, this, totally could just do this. And curses. It's like, it's literally just to keep track of what I do every day. And it's just like it's snippets if from Google's where I picked up the habit of just literally just oh, I did this today that was out of ordinary that decided today that was out of ordinary just to kind of track the high level, not just meanings, but I actually did something different today. And it's like, oh, I could totally do this. In the terminal, like suddenly pull up in curses library. It's in the standard library. And it's like, yeah, oh, yes. You need to know the size of your terminal window and all this other stuff. And oh, how to move the cursor to this specific position. It's like, Yeah, no, I still don't have time for this. So move this along and make input work. I mean, I totally don't forgiving a shot.
07:48 Yeah, quite cool. And if people don't know, you kind of live an alter ego. You've got Brett cannon, open source steering Council, and also work closely with VS code at Microsoft. Right. So having the screenshot on the screen is right at home.
08:04 Yeah. Just for people who don't know, I'm the dev manager for the Python extension for vs. Code. That's that. That's how I pay for my open source habit, which includes being on the Python steering Council and the core deaf.
08:17 Yeah, quite cool. So gandel four, says, I've been using prompt toolkit for the tweet library for a while. But this rich seems a lot nicer if it actually accepted input, which I agree, it seems very cool.
08:29 And I've heard some people using them together. I just don't know how to do that. So if somebody wants to write a tutorial on how to use both rich and pro toolkit together, that'd be cool.
08:39 Yeah, that would be cool, actually. All right. What I want to talk about is this article I ran across and some tools that I went to play with afterwards, after reading it called 12 requests per second. Sounds amazing, right? So it's a it's a realistic look at web frameworks and Python web frameworks. So if you look out there, you'll hear things like, oh, we're getting 100,000 requests per second on uvlo. With a single thread, or, you know, we've got Sanic, doing 100,000 requests, or even depronto, up to 1.2 million requests per second, right, which is amazing. And then a lot of these examples are doing like really not, not real type of work, right? They're just doing some little tiny thing like, oh, here, let's add two numbers and tell you how fast we can do that. So this article is more like, well, let's go through a real application and see what kind of performance you might expect. So I thought it'd be fun to talk about this, because it gives some people some things to think about, you know, how can you compare your app to these types of quotes that people are getting, and so on? And would it be better to choose something like depronto, or a IO HTTP or fast API or something like that over say, flask or Django which don't have so much support for things like async so I thought this was fun, go through and just kind of look at it. They start out by profiling just sort of a Hello World app and they get Alright, well, hello world is pretty good. If you run flask on C Python, you get some, some out here, maybe it was 500 to 1000, you run it on pi pi, they were able to get 3000 during it. And so that's just, you know, flask run, right, which is not really the way you should probably be doing things you should be running on G unicorn, or microwaves gi or something like that. So if they ran the same thing on G unicorn, right, it's up to like 12,000. And you run it on, compare that against Sanic, you get Sanic was doing like 24,000, or something. So these comparisons, I thought would be fun for people you can go through, they've got a bunch of graphs you can check out, but then they write one that actually talks to a database doing database things. And then let me get down to the numbers here, the bottom for that. And they're like, Okay, well, with this one, now we're running at, you know, quite a bit lower requests per second. And I think in the end, they said for our real app, what we ended up with is a whopping, like, 12 requests for a second. Yeah, here we go. Here's the graph I was looking for. were like, this is flask running the synchronous Postgres driver there. And that's all pretty interesting, I think, you know, even be able to answer the question, you know, well, what about me? What about my life? How would it do if I were to run these things against it. And we've talked about locus before, locus is a really fantastic way to write load testing and user interaction, action testing against websites, web API's, and so on. And it's all about Python. So what you do with this is you go through, and you write some kind of class and you say, Okay, here's a test, like a representation of what people might do to my website, right? They might log in, they might go view, the episode page, if we were testing the Python bytes site, they go check out the episode page and might drill into an episode, they might do a search. And what you can do is you write simple little bits of Python, to say, here's a function that does this action. Here's a function that does that action, like, here's one that goes to the episode listing. And then you can actually, it's not shown in the example here, but you can say things like, Well, you know, 10, out of 15 times, I want them to view an episode page, one out of the 10 times, I want them to do a search and three out of the 10 times, I want them to actually go to the listing, right. So you can sort of break it up to what the normal look would be, then you just run locusts and you get like this, this web UI where you enter some information. And then it just runs if you want, even in a distributed way against your site, and then you know, pounds it until it gives up basically, Brett, have you heard this, this tool?
12:36 No, I actually haven't. I mean, you hear about different tools for different things. But this is pretty looking at this example. It's pretty simple, which is pretty great.
12:44 Yeah, the simplicity is really great. And then when you run it, you actually get a web view into it, then the web UI lets you basically control and monitor the tests in real time. So anyway, coming back to some numbers off of that, I decided, well, if they're getting, you know, 12 to 80 requests per second, what is my stuff look like? Like, for example, the toxified entrees stuff? It's, it's not using any of this async stuff? How is it doing? So it's Python three micro is gi pyramid MongoDB on a $20 server? Would it get 12 requests per second? I don't know. So I turned, I turned the thing will loose in it, it'll do about 125 requests per second, I felt better than that's better than 12. I felt pretty good. But one of the things that's cool about locusts is you can tell it, not just I want you to use these, this partitioning, but also I would like you to have a distribution of how often the user actually does something. So you can say, well, this user is going to make between every 10 to 60 seconds, it's it's going to do some one of these things among this mix. So instead of just pounding it, it's like, well, an average user doesn't just hit f5, or Ctrl, Command r as hard as they can they, they move around and they pause right. So you can pause that. So I don't know, maybe 10 to 20,000 realistic users, I was able to sort of map out with this anyway, it was just a fun deep dive into like, well, what is performance look like? How important is async? Some of the tools to do it? I don't know if people are if that's interesting people, Jeff, definitely check out this 12 requests per second article here. I think I think you'll enjoy it. Then also check out Lucas, if you want to try to answer that question for yourself.
14:18 So I was curious how when you're testing your site, can did did anybody else like experience downtime or something while you're?
14:26 Well? So that's the question, right? Like, with the locus thing, you can start to see what the like real time what the response times aren't and everything, it never crashed. It just got a little bit slow. So I was you know, I, I ran it locally. And I'm like, well, what's it look like in production, right? So I'm like, I really want to know, like, what with all the way that everything's put together, what number will I get? So I'm like, I'm just gonna turn it loose on it. And if it starts to overwhelm it, I'll just hit the stop button, you know, and it never died. It just got to where it took, like three or 400 milliseconds to get back to me. So it was okay. Okay, good. So pretty fun. Pretty fun example.
15:00 I think one other thing I appreciate about that blog post actually was they pointed out towards the end that at some point scaling is no longer a Python problem. And it's an infrastructure problem like, yeah, so it's probably better to update your database and stuff. And I've heard this from a lot of people to have, there's kind of a misnomer if you're not in the Python community that Oh, Python, so slow, it's going to be your bottleneck. And instead, I've heard it from a lot of people in the community that Oh, actually, it's not, I can get it done, get my work done in a third of a time. And then the two thirds of time it would have taken me to do it in Java, just choose the language, I can optimize my infrastructure. And guess what I end up with the same performance with more time to bug fix and tests, then I would have another platform. And the other thing is, is obviously there's there is always throwing more hardware at the problem if you need to horizontally scale. But honestly, I think when I say this post, also, I think, from our credit point out, not everyone needs 1000 requests a second.
15:52 Yeah. Like most people don't even popular websites. Yeah. are not doing 1000. I mean, that's a lot of requests. A lot like yeah,
16:01 most people measure inquiries per minute, not per second, right? Yeah. So like, even 12 per second, like we're still talking 725 math is not putting me on the spot requests per minute, right? That's a decent cliff. Like, if you're running a business that's getting that much traffic every single minute, you're probably still doing pretty good for your SEO,
16:20 that's 32 million pageviews per month, right? That's, that's like you're at a limit where you got some real stuff going on some real infrastructure that Well,
16:28 okay, so but there's also if I've got services running, I've got a bunch of services that include this. I mean, one, one user in America interaction isn't one hit, it might be dozens, so
16:40 yeah, so so my rule of thumb is honestly, don't worry, unless you're going over 100 a second, like you should be able to hit 100 a second without too much effort. And then after that, you might have to start doing some planning. But I would suspect as a general rule of thumb, you can you can handle hundreds without a whole lot of effort in Python. If you get into 1000, you might have to do a bit more planning for it. But it's totally serviceable for that kind of thing.
17:01 Yeah, totally agree. Magnusson. Magnus Carlsen has a quick comment. Nice. So I can now see if my work turning my my fast API app into async was worth it. Yes, you can. That's a very good use of that. I'm gonna
17:16 be biased and just say it was totally worth it. Magnus, you just don't know yet.
17:20 Because it's got the cool new design patterns. Absolutely. Exactly. All right, Brett, you're up next, you've got something special for us.
17:28 Yeah. So
17:28 the world exclusive news breaking. Thing is the Python launcher for Unix is probably at RC stage. For those of you don't know about this, it's a project I've been working on since May of 2018. And if you're a Windows user, you probably know what I'm talking about. But on Windows, there's a little app called the Python launcher, for Windows, and it ships with Python, and it gives you the PI command. So sometimes you might see this on websites where they say, Well, if you're on Windows, you run Python using pi, and then the command and other platforms just run with pi, Python three. And I decided that I kind of wanted this on Unix, and had enough people complain to me that like how it's difficult to get started, we're having the windows instructions on how to run something was always different from the Unix version. And all this
18:19 data set is really annoying. The very first thing you got to do like right at the start, like Oh, it's so easy. But there's a few ways there's like this diversion, right? Exactly.
18:28 And I saw some benefit to the Python launcher, because what it does on Windows is it serves kind of two key purposes. One is it kind of makes up for the lack of shibang support on Windows, because the launcher will actually look for a shebang line, figure out what version of Python is required, and then finds the right version of Python to run it. The other thing is it does away with the need of caring about what order you installed your pythons in because like, for instance, on Unix like Python three, what does that point at? Well, what it points at is the last version of Python three that you installed, not the newest version of Python, right. So if you installed three nine today, but then installed the three, eight security release that I think happened yesterday, you will get three eight as your version for Python three. So if you don't specify Python 3.9, you won't be running it like you had to either create your virtual environment and be using it that way. Or remember that Python launcher takes care of all that it will automatically just figure out what versions you have installed and just automatically run the newest version. So I really like that. So I decided to implement it in rust, partially as a rust project of decent size. And it's about 1000 lines of rust code. And also because I wanted it because if this works out, hopefully some projects can stop listening to different ways to run things. So in the Unix version, it's a little different from the Windows version. And I'll ask for feedback from the two of you. And anyone in the live stream if they want to provide it is first it looks to see if a virtual environments activated so it looks for the virtual environment variable and if it's there, it just uses that so Automatically, this is a little shorter and easier if you have an activated virtual environment, just p y. Now, instead of having to type Python,
20:06 the next one is if you're here to save us from RSI already, this I'm kidding. But this is really interesting. Keep going.
20:13 Yeah, one third the typing. The other thing is if you create a virtual environment in a dot e dot v and v directory, it will also automatically use that without activation. So once again, if you use the I personally think standard nomenclature, name, your virtual volumes dot V and V, you don't have to activate them anymore, because activation is just a shorthand for the shell that honestly I typically just use to get make Python do the right thing because I use dash M for everything. Even when I'm in active environment. This takes care of that entire step for me, so I don't have to activate my environments anymore.
20:49 Okay, that's cool.
20:51 After that, it does this shebang, just like on Windows, trying to figure out what the right price I might be for that. And then after that, it checks some environment variables, like if you want to have three point 10 installed. For instance, for Python, you have an alpha installed, but you want to make sure that if you ever say hey, if I say Python, if I run pi with no arguments, or say, hey, I want to run a Python, Python three version of Python, you can actually set the environment variables to restrict it and basically specify the exact version you want. And then after that, it just if none of that triggers, it just finds the newest version and runs that I've been running this personally for a while. And when I edited the V and the V and V support, I've completely switched, I don't even bother to anymore, because this completely fits my workflow of just doing exactly what I think it should do whenever I need. Now the question I have for the two of you, and anyone in the audience, who cares answer is do you think prioritizing virtual environments overshare bangs make sense. Now my argument is yes, obviously, Arosa would have done it this way. But this is hard to be it, I don't know how much of this is a Unix ism versus just in general, because I do hope to actually add windows support to this as well, I make this a universal Python launcher. So the reason I did it this way was on Unix, you can just ch mod a file and have the executable bit. And that's how you can execute it. And honestly, most of my files at this point are going to be in a virtual environment anyway, whether installed via PIP x, or I create an entry point, I install that. So once again, I don't need the shebang directly, it's just going to be baked into the executable, and Windows will do the exact same thing, right, like set of tools will be that little shim. So to me, if I am running in a project directory with a virtual environment, whether activated or not, I'm assuming that's what I want. Don't try to figure it out for me, or else I'd be running this somewhere else. And I would not have a virtual environment installed. So that logic makes sense. Do people do you to think I made the right decision here and choosing virtual environments over shebang lines? Or my crazy?
22:43 I forgot your banks or even supported
22:46 all about virtual environments?
22:47 Yes. Okay. Cool.
22:49 I think this is fantastic. I've long wanted an easy way to just say I'm down inside. Maybe you can tell me if it works this way is does it traverse up looking for a virtual environment? Like if I'm in a subdirectory, and I try to pi a file, but one directory up, there's a virtual environment will go up and find that and then use it?
23:10 No, I did not implement it that way, it would not be too difficult to do it that way. But for simplicity reasons, when I first plug this in, for my purposes, I'm not an SRC guy like brain is for instance, so I don't need to worry about that as much. But that'd be totally reasonable feature request to ask for to be able to traverse up directories looking for the dot v and v direct. Yeah, very cool. I mean, the level of my SRC go up a level to find the
23:36 right like, I have a lot of things that will do like maintenance for my website. So I'll have like a bin folder as one of the sub folders on my website, then I'll go in there and I want to run something if like, Oh, I should have run in gone up. And then bin slash, I think it'd just be really cool. Like, I'm sort of in the realm of this virtual environment, even if it's not at the same working directory.
23:55 Yeah, and one thing I do want to clarify here is this is very much about simplifying running Python manually, it is not meant to completely replace running Python, right? Like a misnomer. I hear from people when I tell them about this is like, Oh, hey, are you going to add a config file to be able to alias to pi pi verse specifically, or this thing or a framework builder. That thing is, and for me is if you have that kind of specific requirement of a specific version of Python you have installed, just use the absolute path to that version of Python. I don't think this is this is purely for, I just want the newest version, I don't care what it is just give it to me or I have a virtual environment, just give me the virtual environment. I don't want to have to think about it. If you have to put thought into what kind of Python you want to run. I think you should be specifying that manually not be using this
23:55 well. So on the side of the being able to go up a couple directories looking for virtual environments. I'm often not in the source directory. I'm often sitting in the tests directory running pi test on stuff in which case I guess I already am in a virtual environment. So it doesn't matter but I'm Just open up another terminal and and, and try to, you know, just run Python. So I like this. I'm excited to hear that this is eventually going to be a Windows thing also, hopefully, I don't actually normally use the Python launcher on Windows, does it? It doesn't do looking for virtual environments does it?
23:55 No, that's a exclusive head is exclusive to the Unix version. So basically, the environment variable it will use, it doesn't actually look on path, it completely uses the Windows registry. It does the shebang. And that's it. So looking in the dot v and v folder, that is entirely my addition. And the order of execution here is kind of tweaked to what I think and it doesn't search on path, actually. So that's a Unix thing, specifically, but I suspect this actually helped with the Windows Store, though. I think Steve dollar fix that issue with the Windows Store install. But yeah, so yeah, there's slight tweaks. But I figured if I have all the logic done already, there's no reason not to just make it universal support multiple platforms. So
23:55 why not? Yeah. So your comment your your announcement here is kind of lit up the live stream. So z docs is just a Brett cannon fan, such as awesome. gala fourth, as is the rest version cross platform. And anyways, like there are a chance of this on Windows?
23:55 No, I don't worry, I have not tried it on Windows. It definitely works on Mac and Linux, right. So it is it truly is Unix based. So it will handle that no problem. I have not tried to wire it up into the registry at all. So that's not there. I haven't opened enhancement issue to do that. I honestly don't know what would happen. If you tried running on Windows. I haven't tried like it. Everything's very hard coded to Unix at the moment just to get this done. Like if you look at the readme, you'll notice everything is slash bin slash. Yeah, there's no scripts support here. If slash one I do windows, and we're off to look at fix, but I have tried put in zero effort because the Python launcher for Windows just comes with install from python.org. Right. So there was no real time pressure to try to get that done. So I haven't tried it.
23:55 I'm a big fan. I can try this on my Mac OS and I'd be happy now.
23:55 Yeah. All right. This is how I do it. I have on my Mac I just installed you do have to install rust to install it right now. Because I haven't figured out how to do distributed by distributable binaries that you can just download. But yeah, if you have rust installed on which you can get through rust up or homebrew or whatever, it's just a cargo install away. So if you want to get a shot,
23:55 please. Okay,
23:55 fantastic. And then I kind of forgot that my Mac was Unix the
23:55 Magnus agrees on the V and V over shibang, awesome gamma forces. Same as well, I don't think I've ever seen a shebang that was more specific than user bin Python three anyway.
23:55 Yeah. And I actually suspect the shibang support was partially because the Python launch has been around back in Python two days, right where that was much more of a concern. So like, as I said, I started this in 2018. So it was pre Python two deprecation. So it's still supported there. But a lot of this, I think, was also partially to support the two to three transitions. So those little extras here I don't think are quite as important. Unless, unless Python four happens someday, which there are zero plans for so I don't Yeah,
23:55 don't hold your breath.
23:55 But by the way, one extra nice nicety I want to tell people is if you're a starship user, the starship cross shell prompt, there is a setting in starship that lets you specify how it figures out what version of Python you have. Now, it does look for virtual land. But otherwise, it has a list of binaries that it will execute to try to figure out what version of Python you have. If you set it to pi. Using this, it will always tell you either what the virtual environments Python version is in the prompt, or whatever the newest version of Python is installed. So as a really nice artifact, because if you read the docs in starship, there's a big tip saying, hey, if you have a Python installed, that's default, ie Mac OS, and it'll always say 2.7 unless you happen to have an activated virtual machine, which always threw me because like, yeah, I'm not using to seven this project. As soon as I added this support for dot v and v files and tweaked my source IP config for this, I always tells me the exact version that I happen to have set up for my virtual environment.
23:55 Okay, it's really handy for those of us not in the know what starship
23:55 so starship is a cross platform. Shell prompt tool. So it's written in rust, but basically what you do is it has instructions for bash and Zhi, Shan fish, and PowerShell and everything else. And basically, it just gives you a line. Michael's Brian's got it pulled up on the screen for those in the live stream. And it's basically just an easy way to just say hey, this one How am I prompt luck? Don't worry about whether it being bash or z or fish whatever. Like I'm a fish user, I'm sure Michael properties issues you're being a Mac OS user like he is Yep. But it shouldn't matter. My prompt is a prompts and pretty much they have pretty consistent user support. So I decided Yeah, you know, I I like some of the niceties that has automatically baked in It does like notification it changed Ron for the usual Oh, I edited my my git checkout, it will tell you what branch you're on all these little nice things. Ah, and sending Fira code as my font gives me all the nice little glyphs I want. And so acid. Nice. All right, cool. It'll tell me what version of Python is currently activated, except for that annoyance of being on Mac. It kept saying, Oh, you've got two points. Oh, until I activate it. And it's like, No, I don't like this. So I prioritize getting the dot v and v done. And then lo and behold, it magically now tells me exactly what I want for Python. Nice. Wow, that's super cool.
23:55 Yeah. Now we know starship. Fantastic.
23:55 Yeah. So before we move on, I'd like to thank our sponsor. So this episode of Python bytes is brought to you by data dog. Are you having trouble visualizing latency, CPU and memory bottlenecks in your apps, and not sure where the issue is coming from or how to solve it data dog seamlessly correlates logs and traces at the level of individual requests, allowing you to quickly troubleshoot your Python application. Plus, their continuous profiler allows you to find most resource consuming parts in your production code all the time, at any scale, with minimal overhead, be the hero that got your app back on track at your company. Get started today with a free trial at Python bytes.fm slash data dog, or just click the link in your podcast player show notes. Ooh, that's neat.
23:55 Yeah, very cool. Thanks. So Brett, I want to take you down a little bit lower. If it's turtles all the way down a few turtles down to this whole to the world and talk about this thing called build a text editor with Python and curses. Is curses. Is that what you do when you get upset? Because it's too low level? Or? Oh, no, it's about the curses, sorry.
23:55 Okay. Well, the Python bites podcasts, there are gonna be jokes like that.
23:55 They're gonna be bad too, by the way. So now, here's a really cool article by wassim, or got about basically walking you through building a text editor like vim or Emacs in the terminal using curses, which is a library that lets you control what happens on the screen much better than like print and log and Warren and whatnot. So there's a couple of just interesting things I thought going through, this might just give people a sense of like, what it's like to work with curses. And then there's this context of this text editor you can play with. So for example, a minimal curses app, all you got to do is define a function, that's your main function, and then while true, go to the screen and get the key. And that's it, you just say get key, and it just gives you whatever it gets pressed. without any help here, there's probably not a way to get out of it. No, maybe Ctrl C. But yeah, there's not like a quit or anything. So you can really easily add a like some tasks and say, Well, if they gave me the key, you know, escape then shows a little colon, you can w q, exclamation mark, whatever to get out of it. However, you want to exit your, your text editor that you're building, and then they talk about how to show stuff on the screen. So if you want to just add a line, you can say at this xy screen, you know, cursor location, just write this line of text on the screen. And it'll it'll add that so really easy. And, you know, one of the things they do is they pass over the file name. And it's kind of meta, like they edit the file, that is the demo for the editor with the editor as they're building it, which is always fun to do.
23:55 So you can do that.
23:55 But then through this article, they go through and they build out like a window class that talks about how big is the screen, this is what Brett was talking about, like, I gotta figure out what my screen looks like, and all that stuff, and also a cursor where you are on the screen, how to have like a view into the file, because you might have like, move to the right a little bit. And the file might be 1000 lines, but you've only got a 40 on your screen or whatever. So anyway, if this is interesting to you, I think you should go through this article and check it out. In the end, you end up with a pretty simple but non trivial little text editor that lets you do stuff. So I think it's pretty neat. What do you guys think?
23:55 This shows me why I really hope will grants brands wish making Richard Fuller?
23:55 Yeah, I do agree, I think if rich is something like that, that has a really beautiful display technology, but also has, like this is cool, because it has really great way to accept user input and like update the screen, but it's very low level. It's like, I don't know, it's OpenGL versus pi game or, or arcade or like you're like at the level of here's what I put at this cursor. This xy coordinate let's put that there.
23:55 Yeah, I think it's a great showing of actually how much work it actually needs to go into making those full text based UI is that you see like, like used to see at the grocery store and those screaming probably still do it so many grocery stores on that screen and just like or when they book your airline ticket, you know, like, Oh, my God, what
23:55 the hell you're using you do this all day?
23:55 Yeah, exactly. It takes a lot of work to make those things happen. It's surprising how much work it is and hence why getting higher level libraries like I think the prompt to quit we were told about earlier and rich and all that would be very welcome in exam.
23:55 Yeah, absolutely. Do you guys either guys know if there's any curses libraries for Windows. Magnus is asking. That's why he's used prompt toolkit before, but I'm not sure actually, neither. Why? Yeah, yeah. Cool. Cool. All right. What's the next one? And Brian, you get the next one. Right.
23:55 Actually, I skipped. So Brett's next.
23:55 I press next and I'll pull up Brett's thing. All right, Brett. Okay. So there's been a major language feature announced that is coming in 310. Right.
23:55 Yes, which actually was in your quick links at the end of Episode 220, which Brian called the five barrel foot gun, which is pattern matching. So one of the things that always happens whenever there's a new Python feature that gets announced, and especially one of this size that gets out into the general community, is some interesting feelings from people. And so this is kind of slash, I will say rant but a little just me as a steering council member, just trying to explain how this came how this all came about where we were coming from as the steering Council in general. I mean, obviously, I'm speaking for me directly. But I generally think how the steering council kind of thought, but also just kind of helping put all this in perspective, because I think for a lot of people, this might be the first big feature that they've seen, because these do this all the time in Python two, but things kind of slowed down for Python three. So as I said, I've seen around the internet, a lot of people ranging from Oh, this looks really cool. I can't wait till I can use it to people screaming bloody murder, that we're killing Python, and oh, my God is the death knell of the language. I've been doing this for some, it's that stressful
23:55 for you. It used to be and you
23:55 It used to be stressful now. It's just frustrating. Yeah. So for instance, I so I gave a talk at pi con us 2018. It was one of the keynotes about participating in open source. And one of the key things I said in it was people just need to try to be kind to each other. It's kind of a very generic thing I know. But it really makes a difference as a project maintainer myself, right, like having people tell me what they think I need to be doing, or what I should be doing is really frustrating, right? Like, I put my personal time and effort into helping make this happen. And the way you respond to that is telling me what I should be doing. Yeah, like, I can only imagine people who are parents having other people tell them how they should be raising their children. It's worse than helicopter parents. It's like helicopter backseat parenting. Exactly. And I and that's kind of the equivalent for open source maintainer is people doing the backseat? Oh, here's what you really should be doing with your free time with what little time you have left on this planet, right? Yeah. So it's, it used to get really upsetting. And now I'm just frustrated and just disappointed that this still happens. It's also a little weird to because in Python, right? I mean, we have 10s of millions of people using language at this point, right? It's, it's measurable. It's just cute. Which means even if you assume one out of 1000 people are jerks, right? Point 1%. Right, like we're still talking 10s of 1000s of people out there who basically know my face name and address and feel totally fine sending me mail on occasion, or just posting out in the comments outside where we all congregate about what they think I should be doing with my life. So it's one of these things where it's just frustrating. Like I I don't
23:55 know, a lot of people feel sorry, but I think when you hear those negative things said to you, even if 1000 people are appreciative, the one negative thing you get feels so much it makes such a bigger disproportion tend to act. Yeah, at
23:55 least, at least at least. And the problem is, it's not relative numbers. It's absolute numbers. And at the scale that we're at with Python, the absolute number of people who just don't haven't quite either, just, I mean, honestly, some people are just totally ignorant that they are speaking this way, and that they don't really realize the way they're phrasing things matters. Or even that, the way they're phrasing it comes off that way, like not everyone who said it should really means for it to come off that way. Thank you, Dean. I appreciate the awesomeness call out. But it's one of these things where I have to bring this up every couple years. And then I usually end up doing a blog post, which honestly, this is kind of a rough draft of the blog post. I'm just talking out loud with all of you. Yeah. And bouncing ideas off all of you for you for the feedback. But it's just it gets a little exhausting that this is something I have to bring up every couple years. If you'd hoped that we'd all learn this by now. It's not like open sources, a new newfangled thing we've all just discovered, but there's still this disconnect between the the consumer slash user slash takers of open source versus the maintainer slash producer slash givers of the community and the disconnect of how to kind of just communicate with each other still there. Yeah. And so specifically, when it comes to this stuff with Python, right is people go like, Oh, my God, this is crazy. And it's like, No, it's not. I've been doing this for 17 years. Everything is controversial when it comes to syntax. True and False. Were controversial,
23:55 right? Like the true and false.
23:55 There were arguments on Python Dev and whether or not we should add those keywords because they would break code that chose to define those cons. As one and zero
23:55 is that's why it's capital T.
23:55 Well, it's also because they're Singleton's. Right. And that's just kind of the practice. So that was really kind of were tied in. I mean, it definitely helped that capital T. Lord. Lord is
23:55 less likely. Yeah, exactly.
23:55 Yeah, but that was an actual discussion we had was, do we want to add Booleans to the language now today? scoffs at that? laughs
23:55 like, What? Are
23:55 you kidding me blends? Really, everything in the language gets debated. And in this too, right? Like everyone's gonna like, Oh, my God, what are you doing? Like? Do you understand the timeline of this? Guido brought this up, I think in July, maybe and had, as one path, got a massive amount of feedback on Python dev on this, went back with his co authors broke it up into three peps 634 635 and 636 so that there was a pep about the design, a pep about the rationale behind the design, and the pep acts as a tutorial to try to make it more more easy to consume brought that out in October, we discuss it on steering council 2020, we made a recommendation that probably the next one should accept it. But we still didn't feel we had enough time to accept such a large thing. Fast forward now to today. We're steering council 2021 had a chance to sit down and look at the recommendation in the peps and have a discussion, including calling Guido and personally at least two times to talk directly to the steering council to help us understand where he was coming from. Like, this was not a snap decision. But everyone acts like it was like right,
23:55 this is literally finally appears on their radar. And they're like, oh, where'd this come from?
23:55 Exactly. Oh, it's like on on Hacker News today. Suddenly, it happened? Like No, no, this has been happening. This has been in discussion actively in public for six months, I first heard Guido talking with someone at a language summit in 2018, about the idea of pattern matching, right? This has been on people's radar as something people have wanted for that long. And I'll fully admit I wanted it, I can't imagine it's something I've wanted for a long time. But it's just it's just one of these things. Once again, people just don't stop and think about the time and effort that goes into all this behind the scenes. It's just in my worldview, this just happened versus Oh, there's probably stuff that happened that I'm just not aware of. And specifically about this, one thing I always notice about this is people need to understand the way the language is designed, right? Like we aim to increase your productivity now to there, that means different things to different people. But in general, it means keeping a language that's easy to understand and small enough to have in your head, but also leads to you being more productive, which also means readability, right? There's a lot of side effects that we all appreciate that come with productivity, but it's not. But that's really the ultimate goal. To me. It's not that the language is readable, readable, makes you productive. And for pattern matching, there are certain algorithms that are just really difficult to do without pattern matching, right? Like the classic examples are parsers and compilers. And they're not easy to do without having pattern matching. There's a reason why functional programming languages that have had pattern matching for a very long time are the best places right now to typically write a parser or compiler because it just the algorithm just works so much better that way. So there were reasons why we wrote this in there will be increases in productivity. For some people, we do realize that it is a big lift. But we also realize not everyone's gonna see this day one, right? This is not it. It is way more than a switch statement, which I know a lot of people typically look at it as right. But the key point is it matches on structure. It's not a match. It's not matching a story just on value, right? This is the big difference between an if statement versus not. And so there was a lot of work behind this. So if there are there are reasons like this was not knee jerk. The other thing I keep seeing about this is people going oh, that's this is not pythonic.
23:55 Why to break it? Well, it is now
23:55 Yeah, I hate to break it to people, but Guido van Rossum the creator of Python. And that's probably the harbinger of what is pythonic is a co author and a big pusher have this Pep right, like Guido helped make this happen. So unless you want to go up to him at pi con and tell Guido that he doesn't know what's pythonic or what's not, I think we kind of just have to go with it that this is now what is considered pythonic. And I will say I have seen people say oh my god, this is what happens when we lose the BD FL like the person who was the bfl made this happen. Right? Yeah, it's not that
23:55 it's not a really
23:55 funny disconnect with people like Guido drove this, like Guido talked to the steering council about this, like he was the point person for this, like he really helped make this happen. So it says pythonic as it can be if you consider Guido the creator and head person in terms of the design of the language, which I will fully admit I still do. I can view Guido dollinger as being bfl is kind of he relinquished bureaucratic overhead costs in his life of the language and the steering Council is more or less taking that on, but I still trust Guido's gut more or less. Like if he really went off the rails, we'd stop them but I don't think this is him going off the rails. I think that girls going like he's that one step ahead of us.
23:55 And as Julian says, I just wrote a horrible if Elif statement, and he's looking forward to pattern matching. Yeah,
23:55 I mean, there there are some real places where I think there's going to be benefits to ways to structure things that will just make things look better.
23:55 Yeah. And Magnus says, grieves me that pythons full spectrum language and it just extends that spectrum.
23:55 But yeah, right. It's Yeah, thanks for that. Magnus. It's it's one of these things. I think some people lose track of that being beginner friendly does not mean only for beginners, then that's where this productivity thing.
23:55 That's I think that's actually a huge part of Python success, right? Visual Basic six was awesome for beginners example. But it just it stopped like, Well, you can't do that here. You You're at the limit, I'm sorry, I don't want to be at the limit. I want to learn c++ Don't make me happy. If
23:55 you want to be beginner friendly, go code and scratch. Yeah, no one wants to write production code in Scratch. It's great to get going. But there's a limit.
23:55 One of the great quotes that I've heard that I think applies to Python is that you can, you can do easy things simply but you can do difficult, difficult things are possible. And I know I got that quote wrong, but
23:55 it's definitely close enough that I'll just go with it. Like, that is very true. Like that is the goal here is to make the the common things easy and simple, and definitely approachable and make the hard things at least possible. And this is part of it. This opens up the possibility for things that were really difficult to do in Python before.
23:55 Yeah, fantastic.
23:55 So yeah, so I'm thinking of doing a blog post about this whole rant called the social contract of open source to try to harp on this whole shoot versus need and just, it's okay. It's it's it, I start to criticism is totally acceptable. It's just be understanding about how you're delivering it. Because we're all on the internet, we do here and there indirectly, you will suffer because guess what that project you're using is going to then suffer based on what you say. So it does matter. Yeah. And just one aside, I think last podcast, Michael said this was influenced from Haskell is actually the design comes a lot from Scala. Okay, for this. But we got this comprehensions from Scala. So we have actually gotten some nice things out of Scala interesting.
23:55 Yeah. Okay. Very interesting. Yeah. Yeah, I mean, my feeling is, it's, it's a big step, I think it's gonna take some took a while to really see the value of it, if you're not working directly in a place where it's one of these algorithms that applies a lot. But the point, I think it'd be good.
23:55 Yeah, it's definitely one of those things that when you see it for the first time, if you if you're not used to using like a language, like most functional languages that have pattern matching, it goes like what, why, and then when you get exposed to when you start to use it more, it starts, much like learning about programming languages starts to tweak your approach to certain algorithms and stuff. And it's just, it's another tool in your toolbox. Really nice. Well, Brian,
23:55 you've got the follow up, huh?
23:55 Yeah. So I'm actually that's why I thought this would be good as a follow up. So we do announced that he wrote the introduction to pattern matching. And, and that it's, it's now part of, it's part of an appendix A of pepp 636. So I went off and looked at it. And it actually really helped me a lot. So this, this introduction, kind of, I actually, I've been trying to follow this path and honestly been confused for a while as to like exactly how it's working. A lot of people have been focusing on the weird parts, but the easy parts actually are really nice. So I really, the there's an example that starts off with that kind of looks like a switch statement, right? So it's a I want to match a status and like an HTTP status, and then I do something different based on 400, or 404 418. And then, and then, of course, we're like, Well, what about default? Right? So there's gonna be some sort of fall through that I handle all the other cases. And there's an example of that. And I was actually curious, the example here shows using the just a single underscore variable, magic variable as a as, as a catch all. And so Brett, I was just curious, can I use any variable here? Does it need to be the single underscore Do you know,
23:55 so I was actually going to come on this when you were done. So the underscore actually has some special semantics here, because in most, most languages that have higher matching the underscore represents the wildcard. This has actually been one of the most controversial parts of pepp 634 was a selection of this to stay consistent with other languages. While I will not suggest to people that there is an opportunity to remove pattern matching, or to necessarily have massive changes to it, one of the things that if people try this out in real world code, and can get feedback to us well before beta one locks in and gets released, because that's our cutoff for future changes for Python 310, which will be in May. This is one of those areas where we could we could actually consider changing, right, like fixing up code to change this case. wildcards syntax of an underscore to something else is totally possible. If we got feedback from the community that yeah, this really wasn't nasty sticking point and really didn't work out well. So I do want to say that there is still a chance to potentially influence the final outcome of how the exact exact thing is, although I don't want to suggest that
23:55 because it's not shipped in 310 yet, right? It's
23:55 exactly it's not shipped in 310 yet, so we can tweak if necessary, but don't come out. I wouldn't suggest people come out and try to completely rewrite the entire syntax. But in this specific case, yes, it is special. Yes, some people don't like it. And if enough people really came forward and really said, this is the one thing I'd want to tweak, we, I think we'd be open to considering.
23:55 Well, the underscoring it's used so much for so many interesting things. Like, why not one more makeup?
23:55 I personally don't care, I just want to know what it is. I mean, I didn't like I didn't like spaces instead of braces at when I first hit it. So
23:55 anyway, but what happens if I put it like x there instead of underscore if you put x so the rules here basically are if you put a single name that has no.in, it, it's a binding, you put a name there with a.it will be a binding. Now, this is one of the things that I learned from Guido in one of our meetings that really helped clarify this for me, and one of the reasons I ended up voting to accept this was let's look at the like, consider these cases, return codes, you're probably not going to hard code 404, four and 14, you're going to import the HTTP dot status module. And you're going to specify the constants from there, right? Do you really want to rebind those constants by accident in your code? No, no, but because it's been imported from a module, or off a class, for instance, you're not going to be wanting to do assignment, you're just going to want to reference it. So if there's a.in, the name, it automatically is just a reference. Now, some people get really hung up on that when they think about the single binding of a name, understandably, but here's the thing is, well, this is this case right now, where if the age is just a bear name, that's a bind, where if there's a.in, it, it's actually going to be a reference in a load to do a value check. There is a potential in the future if people come back after trying out the PAP and having experience with pattern matching to add to the language, a leading dot, which is actually used in Swift, by the way, in Swift community for something else. But the key point is we can make it like dot x to act as a load for comparison for a local now, that's
23:55 an interesting syntactical use, like it's not actually coming from anywhere, but treat it as if it were
23:55 exactly now this was originally in the path. And there was massive pushback from people thinking that they would totally miss the dot. But Cory Benfield, who used who helped us to run requests and all that and has multiple talks from Python, person who helped me start pushing sanzio development of libraries in the community. He's he works at Apple now on swift stuff. And he posted something on Twitter and asked him like, Hey, does that lead dot have any problems with swift? And they said, Actually, the whole community is totally fine with it. There's no readability problems, it just totally works. Because so we could potentially in the next version of Python actually make it so that that that habit adopted the name, whether it's just leading or not, we could actually generalize that. But because it's a forward compatible thing and backwards compatible, we decided not to do that in this path and just started, just keep it simple, simple, keep it simpler than it had to be necessary to go out and then add.
23:55 So if I just put a bear x, it's going, will it hit it? And will it bind to it and assign the value to x?
23:55 actually, that's what I expected. Because as I go down this little tutorial, it talks about like unpatterned, like unpacking, which is totally cool. So let's say you have, like an example is a point object, which is like just an XY tuple. Now, I want to be able to, if I in the case, statements are, you know, specific points that you're going to match to or, like, let's say, you don't care what X is, but you you want to fix y at zero. In in the case, in the case block, you want to know what the value of x was. So you do want that binding. And that's, that's really just cool. I can't wait to play with it. So my, my follow up question. I'm glad you showed up here is when can I play with this? Is this in one of the alphas now or betas? Or do you know,
23:55 don't there's I don't know if it's I don't think it's landed yet. Bram butcher, who's one of the co authors of the pep has a working implementation already, which was, which is actually available in a Jupiter was available in a Jupyter Notebook somewhere. So there's definitely a fork somewhere where you can try this out. Okay. But I don't this is not landed in mainline. So it's not out in alpha quite yet. Um, one thing I did want to point out about this, that's really kind of cool. With the example that Brian showing here, right is the match against a point argument, as he said, that takes a two item tupple. Right. You could actually change this to also accept a single item tuple that represented x&y with the same value. And that would totally match separately as well. Right. So once again, this is matching against structure. So you could actually pass in tuples of different sizes, and have it automatically just kind of maybe do
23:55 something different with 2d or 3d.
23:55 Yeah, exactly. Yeah. Yeah, exactly. So because it's a structural thing and not a value thing. It opens up some very interesting doors. Interesting.
23:55 Yeah. Cool. It's really great to have you here to talk about all this stuff. final comment, close this one out, I guess, as the doc said. I think once good syntax highlighting lands, this will be as natural as list comprehensions.
23:55 Yeah. And that's actually a good point. One of the things we had discussed was Mike the leading dot would, How hard would that be to see with or without syntax highlighting, and we realized that honestly, syntax highlighting will probably do it. One thing some people were a little worried about was, the reason this is even possible was because of the new parser that landed in Python 3.9, the new PGM parser. It can do more contextualized parsing. So that match for instance, isn't going to suddenly make all your match keywords or the match method on from our a module on match objects suddenly not work, it contextually can tell that that match keyword is for a match statement. So there might be a little funky coloring for some people based on how your syntax highlighting works. Like you might still have all your match methods highlighted, like it was the match keyword. But honestly, it'll be from from from what you're looking at in terms of code, it's going to be fairly obvious that is not a match statement. So we weren't too worried about that kind of haziness, as it were when it comes to syntax. Yeah.
23:55 Now, this is cool.
23:55 I know a guy on the vs. Code team. So
23:55 yeah, so I
23:55 have a quick plug in thank you to magic Python, which comes from the magic Python team. Magic stack, the people who do edge dB, they actually maintain the syntax highlighting that we use in VS code, as well as I think is using Sublime Text and add on like they have a generic grammar that they use that they just autogen out to various editors for their syntax, mostly text me for format, but kudos to them for always maintaining that syntax highlighting because that's actually what VS code itself ships in the box. That's awesome.
23:55 Yeah. Nice. Brian, I don't have any extras you don't know. Now, right? Anything else you want to throw out there? Before we make a bad joke? No, just
23:55 thanks for having me on. Thank you, for everyone listening to me rant on about how to please try to talk to maintainers that help them keep their sanity. And that's it. I hope, I hope people give the Python launcher a shot and it works for them. And that's
23:55 very exciting.
23:55 Let me know I can't make any promises about it shipping specifically with Python, but at least it's a tool there that I find useful. And I hope others do as well.
23:55 Cool. I'll definitely check that one out. All right, you guys ready for the joke? I you know, we're, we're all stuck at home and especially if you're single, it's it's really tricky to to get out and meet people and you know, whatnot. See ya see what's gonna make you happy. So I got a joke along those lines to kind of help people out there already. Mm hmm. All right. So there's this tweet by Christina Xue says if you're not happy single, you won't be happy in a relationship. I mean, we've all heard this how important it is to be just like content with yourself. She says no, true happiness comes from closing 100 chrome tabs after solving an obscure programming bug, not from other people. Come on.
23:55 Yeah, anyway,
23:55 I thought that was good. And Kiki? Are you to the code for
23:55 folk who have like a bazillion tabs open at any one time? Are you more like only keep open exactly what I'm working on. As soon as I'm done, I close them.
23:55 I not 100 tab person. But what I do sometimes I'll like leave the tabs open. Like I'm not done with this. So close it if I got to reboot the computer, the first thing I do is reopen the browser and say history restore previous session because I'm like, there were things in there. I don't know what I was doing. But I know they're important. So I may have views that a little bit
23:55 high. But my daughter came to me once and said I need to add like finals. And then my laptop is too slow new laptop, like Okay, well let's talk about what you're doing. Well, she was doing like three research projects and had like 100 tabs open. Cool. Let's just try this first. Can I can I tell you about this cool thing called bookmarks. Anyway, totally fix it. Yeah, that's
23:55 awesome. Your dad showing bookmarks to the youngsters?
23:55 Yeah, Magnus says the number of tabs is a measure of how hard the problem is. And Niels says great rant. Lovely work. Brett. Great to have you on the show. Brett. Always happy to be on. Thanks for being here. And thanks, everyone. Brian, thank you. Thanks. As always, bye Oh,