Transcript #329: Creating very old Python code
Return to episode page view on github00:00 Hello and welcome to Python Bytes, where we deliver Python news and the headlines directly to your earbuds.
00:06 This is episode 329, recorded March 30th, 2023. I'm Brian Okken.
00:13 >> I'm Michael Kennedy.
00:14 >> Before we get started, I want to do a couple of things.
00:18 I want to thank Microsoft for Startup Founders Hub for sponsoring this episode.
00:21 Please listen to their spot.
00:23 We've got a very special random guest appearance or voice for that ad read.
00:29 So that'll be fun.
00:30 I also, if you're listening to this, I'd like to encourage you to--
00:35 and you've never watched the live show--
00:37 I'd like to encourage you to watch our live show on YouTube or streaming.
00:43 Today is Thursday, but we usually record Tuesday at noon or 11.
00:47 No, Tuesday at 11.
00:48 Yeah, I should have practiced this.
00:49 Anyway, but welcome.
00:52 And the people that are here today are watching on YouTube.
00:56 Thanks for watching.
00:57 And why don't you kick us off, Michael?
00:59 I got something, I think it'll resonate with you folks.
01:03 Maybe you already are aware of this.
01:04 This is news to me.
01:06 Comes to us from Brandon Hannigan.
01:09 So thanks for sending that in.
01:10 And it's an environment variable.
01:13 And in and of itself, it's just interesting that it leads to many more environment variables you can play with is also pretty awesome.
01:19 So this one is about the Dunder Pie Cache folder.
01:24 So I don't know how you feel about these, Brian.
01:26 I'm glad they exist.
01:28 I'm not necessarily super glad that they're spread out a bunch of random folders.
01:34 So like a lot of my projects have many different modules and different folders, you know, sub module type set up.
01:39 And when I run them, they all get filled up with DunderPyCache folders with startup PYC, you know, compiled Python, like people might think.
01:48 I think Python is compiled, but yes, Python is compiled.
01:51 It's just then interpreted not, you know, all the way to machine instructions, right?
01:55 So those files end up in the DunderPyCache, which spread throughout your project structure.
02:00 Sometimes I wanna make a copy of that.
02:02 I wanna move that around.
02:03 I wanna zip it up and share it, and I don't want those things in there, and so I'll have to go in and search for all those and remove them recursively, which is not the end of the world, but I'd rather they are not there, or if they were there, could they just go in a top-level thing?
02:15 You know what I mean?
02:16 Like, could they just go into a single Dunder Pie cache that understands the whole structure, not every file being treated as if it's unrelated?
02:24 - Yeah, I mean, it's better than it used to be having the PYCs right next to the Python files.
02:29 >> Yes. It is an improvement.
02:32 >> Yeah.
02:32 >> But this thing that Brendan said, you can set the Python PyCache prefix, which is a path, maybe it should be called Python Cache folder, I don't know.
02:43 Anyway, what you do is you set this in your environment variable, maybe in your user account, maybe in the activation of a virtual environment.
02:52 If you want it to be a one-off type thing, I don't know.
02:54 Then when Python goes to create these, it goes, oh, they don't want the DunderPy cache.
02:59 They want it over in this directory over there.
03:02 And so it'll make, you can isolate all of your DunderPy cache stuff into a separate location on your user profile.
03:09 You can go and just blast that away whenever you feel like.
03:11 But most importantly, it's not within your source code.
03:14 If you like zip it up and hand it out or things like that.
03:17 - Oh wow, okay.
03:18 That's great.
03:19 - Yeah, it says, if this is set, Python will write star.pyc files in a mirror directory tree at this path instead of in dunder pi cache directories within the source tree.
03:30 This is equivalent to specifying the -x pi cache prefix equals path option.
03:35 So pretty cool, right?
03:36 - Yeah, I also didn't know about the -x though either.
03:38 So that's pretty cool.
03:40 - Yeah, this page that I'm linking to is at the top, it has all the command line options and then almost all the command line options have an environment variable thing if you want it to just be the default all the time and you don't have to set it.
03:52 So there's a bunch you can come through here.
03:54 So Python path, you can set Python startup.
03:58 So these are the Python commands that'll execute whenever you start Python, which is kind of interesting.
04:04 Optimize, breakpoint, debug.
04:07 The one we talked about, you can set up a hash seed so you can get repeatable deterministic hashing.
04:14 A bunch of stuff is here people can check out.
04:16 How you look at warnings, how much warnings you wanna see, whether or not there's buffering.
04:21 So, you'll see sometimes things like the standard out will come after the standard error, but the standard error in time actually came after the out, 'cause the buffering got out of, they hit the buffer at different periods.
04:36 So you can do things like turn that off, turn on malloc stats if you wanna track that.
04:40 So there's like a bunch of things you can come in here and play with, but I think the PYC one's an interesting one, especially for people who hand out code.
04:48 If you're doing a training or a tutorial and you're like, "Here, I want to give everybody this.
04:52 You got to have the pyc file." >> I was just looking through there because I'm surprised that you can't specify which XKCD comic to link to.
05:00 >> I know. When I import anti-gravity, which one?
05:04 Don't worry, we'll get there.
05:06 Well, that's what I got for this one.
05:09 The Python PyCache prefix. Check it out.
05:12 >> Nice. I wanted to talk about GUIs.
05:15 We haven't talked about GUIs for a while.
05:17 >> It's true. We were on such a kick.
05:20 Yeah, but several people have mentioned this to us, so we thought we'd cover it.
05:26 It's a package called NiceGUI.
05:28 And normally, actually, when I think about GUI, I think about like actual, not web stuff, but user interface stuff that's on the desktop.
05:36 But this is a browser-based thing.
05:38 So this is a package that it says it's easy to use Python-based UI framework, which shows up in your web browser, buttons, dialogues, markdown, 3D plots, and more.
05:48 What's cool is you can play with it all before you even try it.
05:53 The documentation is really great.
05:56 Even just here, I thought this was just like a screenshot or something.
06:00 No, you can just like, this is part of it.
06:02 You can move it and interact with it right here in the first page. It's pretty cool.
06:07 The full documentation is really pretty great too.
06:11 I actually want to try to play with this because the code really looks pretty easy to write.
06:15 So for some quick maybe dashboards or some quick control stuff that you're okay with doing through a web browser, why not try it out? And some of these are pretty cool. I was playing with text input. So it's talking about validation stuff. So you can have some text and you can just start typing and it's saying, "Oh, that's too long." So you can, I mean, this validation is pretty simple of just checking length, but you can do all sorts of stuff like email validation or or whatever you want it to do, because it's just like a function that's calling.
06:45 So kind of neat.
06:47 You got a validation there.
06:48 Knobs, knobs are fun.
06:50 I was playing with the knobs.
06:51 So drag knob and turning.
06:54 Anyway, all sorts of fun things.
06:57 - Context manager to put it into the page with knob.
07:00 - What?
07:01 You go with knob.
07:02 Interesting, I wonder why.
07:05 - I don't know.
07:06 Oh, to put the icon inside it.
07:08 So you basically, it looks like you're focusing the subsequent commands to be within the container of the knob because the knob is like a like a circular progress bar type thing and you can put a volume up icon inside it.
07:21 Okay, anyway, just all sorts of cool stuff. Joystick. That's really, I don't have a joystick to play with this out, but some interesting naming there with the joystick. But anyway, so moving on, date pickers and all sorts of things.
07:40 Oh, that's nice.
07:41 Cool.
07:42 But UI elements, if you're okay with trying something new, a nice GUI might be right for you.
07:49 >> That's interesting, isn't it?
07:51 When I look at these types of frameworks, a lot of times I feel like what they say is, HTML is terrible, the DOM is terrible, CSS is terrible.
08:00 Let's create a parallel Python or name your language equivalent, where we put elements in the web page.
08:07 I'm like, they may have their drawbacks, but at least you have a ton of tools and stuff that apply to HTML and CSS and all those things, right?
08:15 But with this one, I think there's a lot of cool widgets and stuff that are here, and it looks more like, it's not like we don't like HTML, so let's make a Python DOM that you create the HTML with, but rather, how do we make a cool interactive page based on these additional things like knobs and joysticks and sliders and stuff that's not easily part of HTML?
08:37 - Yeah, and the places where I would really use something like this are, I mean, this is a short code.
08:44 So especially internal tools, or even just stuff for myself.
08:49 If I want to explore some data control, like a database, or got a bunch of, you know, controlling some system or something, and I want to like quickly throw something together, something like this would be great for just doing like a one-pager something to try it out.
09:05 - I also think these types of frameworks would be pretty cool to bring into some kind of electron JS type thing, where you're like, and here's how you make it an app that doesn't actually look like a web page and give it to someone.
09:19 - Yeah, so one of the things they bring up is like, great for micro web apps, dashboards, robotics projects, like school robotics, stuff like that, smart home solutions. - Probably that joystick thing, right, can drive you a little.
09:32 - And then one of the nice things that I noticed about the documentation is they've got a bunch of demos.
09:40 Oh, these are the actual demos, but there's examples.
09:44 Where did I find this?
09:45 Is it maybe under examples?
09:47 Well, anyway, there's a whole bunch of like actual code.
09:49 So there's repos around that you can try it out with different repositories.
09:56 Maybe it's just on the front page.
09:57 Anyway, I was impressed.
09:59 Oh yeah, here we go.
10:00 Down at the bottom of the front page, there's like slideshows.
10:02 And even if you want to, will this work with FastAPI?
10:05 apparently there's a FastAPI example for just some quick repositories so you can try it out yourself.
10:13 - Oh, maybe like an admin page type of dashboard thing that you can play with.
10:19 Yeah, there's some OpenCV webcam.
10:21 - Infinite scroll for galleries.
10:24 And the amount of like demos of components right there, but then actually specific examples where you can, with the code, with repos that you can just copy and get started with.
10:37 That's pretty impressive that they put all that together right off the bat.
10:40 - Yeah, it looks like it's definitely worth checking out.
10:43 - Do you know what else is worth checking out?
10:45 - Our sponsor.
10:47 - Yes, Microsoft for Startups, Founders Hub.
10:50 Thank you for sponsoring this episode.
10:52 And what was the key that you, how did you generate this code, the text?
10:58 - Well, remember, I don't recall who suggested it because I had the American football coach motivational speech version, variant last time.
11:06 And somebody said, well, what if it was like Ted Lasso?
11:09 So I said, hey, open AI thing, here's the Microsoft ad.
11:14 Could you rewrite it this time in the style of Ted Lasso?
11:17 - Yeah, so it's an odd episode.
11:21 So I get the honor of trying to be Ted Lasso, which I'm not gonna get the voice, so apologies.
11:26 And I did not grow up in the Midwest.
11:28 So anyway, let's just get started.
11:30 Hey there team, gather around because I've got something real special to share with y'all.
11:35 Now, you know how much I believe in the power of teamwork and positivity, right?
11:39 Well, this opportunity I'm telling you, about to tell you is just like the perfect assist to your startup goal.
11:47 I'm talking about the Microsoft for Startups Founders Hub, folks.
11:50 Now imagine if you will, a locker room full of support for your startup, especially if you're keen on that there artificial intelligence stuff.
11:58 We're talking over six figures of benefits that'll change the game for your team.
12:03 They're offering you 150K in Azure credits.
12:07 And that my friends is like having the best player on the field on your side.
12:11 And what's more, the Founders Hub has given y'all a unique chance to access open AIs, APIs, and the new Azure Open AI, API, the Azure Open AI service.
12:23 It's like having your own generative AI coach to help you come up with game winning strategies for your applications.
12:30 Now, I know how important it is to have the right support and that's why the folks at Microsoft are also offering one-on-one technical advice, helping you with your game plan, scalability and security.
12:42 Plus, you'll be part of a network of mentors who know the startup world like the back of their hand.
12:49 I can't emphasize enough how amazing this opportunity is, friends.
12:53 And guess what?
12:54 It's open to everyone, no matter what stage your startup's at and no funding requirements.
12:59 Just take five minutes to apply and you'll be on your way to reaping some massive benefits.
13:04 So come on team, let's harness the power of AI for your startup and join Microsoft for Startup Founders Hub today.
13:10 Head on over to pythonbytes.fm/foundershub2022 and sign up.
13:16 This is your chance to score big.
13:17 So don't let it slip through your fingers.
13:20 And just so you know, the ad you just heard was written by the same AI you'll get access to.
13:26 Ain't that something?
13:27 So don't wait any longer and sign up at pythonbytes.fm/foundershub2022.
13:33 A big thank you to Microsoft for supporting this show.
13:37 - That open AI, that sure is something.
13:39 All right.
13:40 (laughing)
13:42 Speaking of something, let's talk about ngrok.
13:44 Let's talk about Flask.
13:45 Like I can't decide, let's talk about both.
13:47 So this one I wanna cover is an interesting one.
13:50 So I've talked about ngrok before.
13:53 For those of you who don't know, like unfortunately their website, I don't know what's gone on, but they've redesigned it in a way they can't really tell what it does, but it's just, anyway.
14:01 In Grok, what it does is it lets you run a command locally and then share your web app, whether that be for an API, someone needs to talk to the API, or just the web app itself.
14:15 So for example, Brian, imagine you had created a cool demo of that nice GUI thing, and you wanted to let some people, you're in a meeting with your team, "Hey, you guys, y'all should check this out.
14:26 This is really, really cool." What you might do normally would say, "Well, let's just fire up a screen sharing and I'll drive it around." But the interaction part of those widgets is really cool.
14:37 So it'd be better if you could just say, "And interact with this." So if you fire up ngrok, you just give them a URL that reverse SSH tunnels into your machine, and then they can access it on the Internet with their browser, and everyone can play with it live.
14:51 So that's really cool.
14:53 I recently used that for, I'm just about to release a course called Python Web Apps that fly with CDNs.
15:01 Like basically how do you do really awesome stuff with CDNs plus Python and Flask and all those things to make your app way, way faster.
15:08 Well, in order to put that together and test it, you've got to let a public CDN get access to your dev machine, which like, how does that happen?
15:16 ngrok is how that happens.
15:17 Same thing with our mobile apps, like, you can see, like right here, we had this problem where some of the data wasn't being passed through as headers correctly to the server and we're like, we cannot figure out why this is not working.
15:29 It's clearly in the headers collection.
15:31 Why is Python not seeing those?
15:33 And it turns out there was like a weird case sensitivity issue or whatever.
15:37 But I just fired up ngrok, pressed debug on PyCharm and said, all right, try it again.
15:41 And then boom, I'm like stepping through, like on a mobile device, I'm stepping through its interaction with the APIs.
15:47 I'm like, oh, I see, here's what's happening.
15:49 And then we fixed it, super easy.
15:50 All of that is to set up Flask-ngrok.
15:54 So all of those benefits are awesome, but what I gotta do is I have to go fire up ngrok, overdo the thing, and then come back, right?
16:01 So it'd be cool maybe if I could just press go, either Flask run, or just go in PyCharm or VS Code, and it would just, in addition to starting up Flask, it would also start up ngrok, pointing back at whatever the right port is, right?
16:16 So basically that's what this is.
16:18 you just wrap the app in run with ngrok.
16:21 So you get a run with ngrok app.
16:23 And then when you say flask run, it fires up the local version, but it also fires up an ngrok URL that you can share with people.
16:31 - Oh, cool.
16:32 - Yeah, so not a huge, huge feature 'cause sure you can run ngrok on your own, but I think it's kind of cool.
16:38 Like basically that means whenever you run your flask code, your flask app for debugging or for dev or whatever, there's always a publicly accessible address that you can share with other people or you can type into some other tool.
16:52 I wanna validate an RSS feed.
16:54 I want to have my API, some API client that is not on my machine like a mobile app or some other, think if this, then that, or one of those types of things.
17:06 All those can just come back right in 'cause you always have this public address available, which I think is pretty cool.
17:11 - That's pretty cool.
17:12 - Yeah, so if that sounds useful, people can check it out.
17:15 - Oh man.
17:16 - Okay, so I was just thinking, I wonder if random address, 'cause like I need another URL that I'm not doing anything with, but I was wondering if random address was taken and random address is taken.
17:26 - Is random addresses?
17:27 (laughing)
17:30 - No, we have enough.
17:31 I've got several that I'm not using.
17:35 - Yeah, that's awesome.
17:36 All right, cool.
17:37 Anyway, people can check this out if they are doing a lot with ngrok.
17:41 So by the way, one thing that I think would be interesting, I didn't see in the docs whether or not this is easy, possible, impossible, whatever.
17:48 One of the things you can do is you can set it up so that this random address is repeatable.
17:54 Otherwise, if you just rerun it, you'll get a new random address, which you gotta keep typing in by doing like subdomain type things and stuff.
18:00 If you've got a paid account, I don't know if it's possible to have it do that or not, but it would be cool if you could make it random, but not completely random.
18:07 So anyway.
18:08 - Not completely, just random once.
18:10 - Yeah, random once, and let's stick with that for a while.
18:13 All right, what do you got for our last one?
18:15 - I want to talk about async.
18:17 So Will McGugan wrote an article called No Async with Python.
18:23 No async, async.
18:25 And that confused me, but it's a really well-written article.
18:30 There's times where if you want to take advantage of async, you kind of have to have async all the way up and down the call stack, right?
18:39 Or you maybe, that's what it seems like at least, to make sure this all works.
18:45 But, and so that's actually what I guess Textual did at one point is made, asynced all the things, but Textual now is async optional.
18:55 And so this article discusses how they do that.
18:57 And the first part is if you're passing in a callback to, if you're providing a mechanism for somebody to pass a callback in, and that callback can, you want it to be either just a normal function or an async function, He's utilizing the await me maybe pattern that he borrowed from Simon Willison.
19:17 So Simon Willison wrote about this a couple of years ago and he shows, scroll almost to the bottom.
19:24 There's this, basically there's a way to, you have an async function and it calls.
19:29 So the caller is async and you're calling something that could be either async or not.
19:34 And you just call something and check to see if it's a coroutine and then await it or don't await it.
19:40 And that's pretty much what Will is showing, is inspect the callback, inspect the result to see if it's awaitable or not.
19:49 Doing it a little bit different method, but similar sort of effect.
19:52 So that's neat.
19:54 So you can provide a mechanism to add a callback that could be async or not, optional.
19:59 But the other part around is, if you're providing an async service that could be called in either an async or non-async, you want it to be called by anybody.
20:10 Because sometimes, like he gives an example of mounting a new widget into textual.
20:18 The caller might want to care, might care about when that's actually done.
20:22 So they might wanna wait for that.
20:23 But they might not, they might just like keep going because apparently textual handles it all correctly anyway.
20:29 They won't let something happen.
20:31 But the caller might not care about when it's done.
20:34 So to be able to allow both async and non-async callers to call an async method, that's a little bit yuckier code, but he provides it.
20:43 So there's this class, this await mount option.
20:48 So there's, I'm not gonna walk through all this code, but basically there's a way to do it, and Will has it, has the method to allow you to have.
20:56 And I think that's kind of neat to be able to provide services and APIs that can be called both in async and non-async ways.
21:05 Now, hopefully, this still is kind of ugly, So hopefully as a community, we can come up with a little bit cleaner solution, but at least there's a solution.
21:13 So it's kind of nice.
21:15 Yeah, Will did a nice job of this.
21:16 And I think it's really a huge benefit that Textual has the ability to be async, but doesn't force you to be async.
21:23 Because if you're already writing async code, you would like it to be because it's a benefit.
21:27 You can do more in parallel, be more responsive.
21:30 But if it means I have to take my non-async code and now convert the whole thing to know about some of the parts of it being async, well, that's a hassle.
21:38 And I think one of the things that drives me nuts about Python's async, there's a few little things that just make me crazy about it.
21:45 It's like, well, it's so close to awesome, right?
21:49 And much of it is awesome.
21:50 But for example, if I have, if I'm in a function and I want to say, here's some async code, I want to just run it here.
21:57 So for example, asyncio, you know, get event loop, loop.run sort of thing, or just asyncio.run to complete.
22:04 If it's already being called within an async function with some other event loop, and you don't know about it, under certain times it'll crash and say, "There's already an event loop," or "There is no event loop." You're like, "Well, give me, if I don't have one, give me one.
22:19 If there is one, just give me that.
22:20 I don't care. I just need to run something async." There's always this weird, I'm not really sure what state I am, and if I get it wrong, then it crashes and I was like, "Ah." I think that makes it challenging to go.
22:33 This part, we're just going to isolate off the async, which it sounds like Will did here, which is cool.
22:39 So that's really excellent.
22:41 Yeah.
22:42 Really quickly here, too, I've had a couple of shots at this myself as well.
22:47 Nothing I've published really very much in terms of what Will's doing or what Simon talked about.
22:52 But for example, on FastAPI Chameleon and the Jinja equivalent, let's you just put a decorator on top of a FastAPI function and then it returns the HTML view of that and stuff.
23:05 But those FastAPI functions, they can be synchronous or they can be asynchronous.
23:10 And so what it has to do is it has to say, is this function a coroutine?
23:15 Okay, the decorator has to return also an async one.
23:18 Otherwise, when you say async and it becomes not async, that's wrong.
23:21 But if it's async one, you can't return the async, right?
23:23 Like, so you got to juggle this a lot, which is kind of a pain.
23:27 And then the other one, I created this thing, which I put up just as a gist, that like just lets you say this async function, we're going to run it in a way that won't have a conflicting event loop complaint by constantly managing the background thread and just pushing the work over there and pulling the results back.
23:42 So yeah, it's interesting.
23:43 But yeah, that's a cool article.
23:46 Yeah.
23:46 Do you remember the "Call Me Maybe" song?
23:49 "Call Me Maybe." Yeah, I do.
23:52 Yeah.
23:52 So Chris May added, "Hey, I just defined you and this may be async, But here's my variable, await me maybe.
24:00 Yeah, so nice.
24:01 >> Well done, Chris.
24:03 >> Well done.
24:05 >> I love it. I love it.
24:07 Well, I don't have any extras, Brian. You got any?
24:09 >> I do. We'll try to make them quick, though.
24:11 So PyPI has a blog now.
24:13 Oh, let's pop over to here.
24:15 PyPI has a blog now.
24:17 So anyway, go check it out.
24:19 There's a welcome article.
24:21 So that's nice.
24:23 Neat.
24:24 And then, okay, so another extra.
24:26 Apparently a docker.
24:27 No, they're laying off plans of charging people for the free team plan, which is kind of a bummer for people like me that paid for it anyway.
24:37 But, you know, anyway.
24:39 So that's cool.
24:40 Maybe I won't have to pay next year.
24:42 I guess they're offering refunds or something, but I'll look into it.
24:45 I guess there must have been a big backlash.
24:47 I haven't been tracking this, but...
24:48 Oh, yeah.
24:49 I mean, like, it's been a scramble all over the place of people because there's sometimes it's a very minimal interaction with it and then suddenly we have to pay for it and you got to figure out how many users and how many seats and all that sort of stuff and yeah or if you want to use it without so if you if you want to use it without the user interface you can use it for free but if you like people asking are are you using it yes we are because we debug with it and everything so so i'm glad they're backing off i still want to i I mean, of course, it's a great service.
25:21 They should be able to make money somehow, but there should be.
25:25 So it's good news.
25:26 (laughs)
25:27 I only, the only thing left I have is a joke.
25:31 Do you want to do mine first or yours first?
25:33 - Let's hear yours first.
25:35 - Okay.
25:35 It's just sort of, I was looking up, I was looking up some documentation for pytest-cov and noticed at the bottom, so there's a, there's mark, it provides a no cover marker, which is nice.
25:49 so you can say don't cover this test. And then there's a fixture. You can also use that as a fixture. But then there's also the no cover fixture, but there's a cove fixture, which, why would you use that? Well, it says for reasons that no one can remember, there is a cove fixture that provides access to the underlying coverage in an instance.
26:08 Some say this is disguised as a foot gun and should be removed. And some things, some think mysteries make life more interesting and it should be left alone.
26:18 >> I love finding stuff like that in documentation.
26:21 >> Some think mysteries make life more interesting.
26:23 Indeed, they do.
26:26 >> All right.
26:27 >> I got a quick one for you as well.
26:29 This one, you knew an XKCD was coming.
26:32 Good reference earlier. This has to do with some deep thinking into how to make your code last so long that it becomes legacy code, and people can use it for a long time, and maybe even curse its name a little bit.
26:45 So there's two parallel universes here.
26:48 On one, this woman just wrote this code.
26:51 She says, "It took some extra work to build, but now we're able to use it for all of our future projects." And the caption for that is, "How to ensure your code is never reused." (both laughing)
27:03 The other alternate world is, "Let's not overthink it.
27:06 If this code is still in use that far in the future, we'll have bigger problems.
27:10 How to ensure that your code lives forever." - Yeah.
27:14 The hover is surely no one, everyone will recognize how flexible and useful this architecture is.
27:20 Spend a huge amount of effort painstakingly preserving and updating this garbage I wrote in 20 minutes.
27:25 Yeah.
27:26 Well, I mean, it's, there's so many examples of that, isn't there?
27:30 I mean, oh yeah.
27:31 Yeah.
27:32 I mean, internally there's tons.
27:34 There's like, oh, there's this, throw this thing together, build script or whatever.
27:38 And it's just, we'll rewrite it later.
27:40 10 years later, we haven't rewritten it.
27:43 Things like that.
27:44 I mean, flask was like a really quick hack, wasn't it?
27:47 Oh, like a joke at first or something.
27:49 I think it was an April's Fool's thing.
27:51 Yeah, I think so.
27:52 Anyway, and the the other side is the lesson that I think people should learn is planning on reuse is just a mistake.
28:02 And I've I've been in many, many design meetings where it's like, let's not let's not plan to six years out into the future on this.
28:11 This that's ridiculous.
28:13 We don't even know because I've also seen people plan for it and it is reused and it is maintained, but the things that you thought you were going to need to be variable are not the things that really need to change in the future.
28:24 It's something else.
28:25 So yeah, somewhere in the middle there lives a let's not overthink this, get it out there.
28:30 Oh, let's take a moment and refactor it.
28:32 So it's like more reasonable in the way we now know it needs to be carry on.
28:36 So my advice, keep, keep the interface simple, keep it minimal, document it and test it.
28:42 And then if it grows, great.
28:43 So yeah, excellent.
28:45 Thanks for that.
28:45 That's funny.
28:46 Yeah, for sure.
28:47 And thanks, everybody, for showing up.
28:49 And thanks, Michael, again, for showing up.