WEBVTT 00:00:00.001 --> 00:00:06.020 Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to your earbuds. 00:00:06.020 --> 00:00:11.200 This is episode 329, recorded March 30th, 2023. 00:00:11.200 --> 00:00:13.080 And I'm Brian Okken. 00:00:13.080 --> 00:00:14.080 I'm Michael Kennedy. 00:00:14.080 --> 00:00:18.220 And before we get started, I want to do a couple things. 00:00:18.220 --> 00:00:21.760 I want to thank Microsoft First Startup Founders Hub for sponsoring this episode. 00:00:21.760 --> 00:00:23.160 Please listen to their spot. 00:00:23.160 --> 00:00:29.280 We've got a very special random guest appearance or voice for that ad read. 00:00:29.480 --> 00:00:30.300 So that'll be fun. 00:00:30.300 --> 00:00:37.040 I also, if you're listening to this, I'd like to encourage you to, and you've never watched the live show, 00:00:37.040 --> 00:00:41.620 I'd like to encourage you to watch our live show on YouTube or streaming. 00:00:41.620 --> 00:00:47.000 Today is Thursday, but we usually record Tuesday at noon or 11. 00:00:47.000 --> 00:00:47.940 No, Tuesday at 11. 00:00:47.940 --> 00:00:49.780 Yeah, I should have practiced this. 00:00:49.780 --> 00:00:51.780 Anyway, but welcome. 00:00:51.780 --> 00:00:55.920 And the people that are here today are watching on YouTube. 00:00:55.920 --> 00:00:56.940 Thanks for watching. 00:00:56.940 --> 00:00:59.180 And why don't you kick us off, Michael? 00:00:59.180 --> 00:01:00.300 I got something. 00:01:00.300 --> 00:01:02.600 I think it'll resonate with you folks. 00:01:02.600 --> 00:01:04.540 Maybe you already are aware of this. 00:01:04.540 --> 00:01:05.760 This is news to me. 00:01:05.760 --> 00:01:08.980 Comes to us from Brandon Hannigan. 00:01:08.980 --> 00:01:10.440 So thanks for sending that in. 00:01:10.440 --> 00:01:12.800 And it's an environment variable. 00:01:12.800 --> 00:01:14.680 And in and of itself, it's interesting. 00:01:14.920 --> 00:01:19.180 That it leads to many more environment variables you can play with is also pretty awesome. 00:01:19.180 --> 00:01:23.960 So this one is about the Dunder PyCache folder. 00:01:23.960 --> 00:01:26.540 So I don't know how you feel about these, Brian. 00:01:26.540 --> 00:01:27.940 I'm glad they exist. 00:01:28.220 --> 00:01:34.260 I'm not necessarily super glad that they're spread out a bunch of random folders. 00:01:34.260 --> 00:01:39.540 So like a lot of my projects have many different modules and different folders, you know, submodule type setup. 00:01:39.540 --> 00:01:47.640 And when I run them, they all get filled up with Dunder PyCache folders with startup PYC, you know, compiled Python. 00:01:47.640 --> 00:01:51.480 Like people might think, I think Python is compiled, but yes, Python is compiled. 00:01:51.480 --> 00:01:55.160 It's just then interpreted, not, you know, all the way to machine instructions, right? 00:01:55.160 --> 00:02:00.220 So those files end up in the Dunder PyCache, which spread throughout your project structure. 00:02:00.220 --> 00:02:02.080 Sometimes I want to make a copy of that. 00:02:02.080 --> 00:02:02.920 I want to move that around. 00:02:02.920 --> 00:02:04.180 I want to zip it up and share it. 00:02:04.180 --> 00:02:05.500 And I don't want those things in there. 00:02:05.560 --> 00:02:10.740 And so I'll have to go in and search for all those and remove them, you know, recursively, which is not at the end of the world. 00:02:10.740 --> 00:02:12.320 But I'd rather they are not there. 00:02:12.320 --> 00:02:15.220 Or if they were there, could they just go in a top level thing? 00:02:15.220 --> 00:02:16.200 You know what I mean? 00:02:16.200 --> 00:02:24.480 Like, could they just go into a single Dunder PyCache that understands the whole structure, not, you know, every file being treated as if it's unrelated? 00:02:24.480 --> 00:02:25.020 Yeah. 00:02:25.020 --> 00:02:29.520 I mean, it's better than it used to be of having the PYCs right next to the Python files. 00:02:29.520 --> 00:02:29.980 Yes. 00:02:29.980 --> 00:02:31.840 It is an improvement. 00:02:31.840 --> 00:02:32.540 Yeah. 00:02:32.720 --> 00:02:39.980 But this thing that Brendan said in, you can set the Python PyCache prefix, which is a path. 00:02:39.980 --> 00:02:42.300 Maybe it should be called Python Cache folder. 00:02:42.300 --> 00:02:42.820 I don't know. 00:02:42.820 --> 00:02:51.640 Anyway, what you do is you set this in your environment variable, maybe a new user account, maybe in the activation of a virtual environment. 00:02:51.640 --> 00:02:53.920 If you want it to be a one-off type thing. 00:02:53.920 --> 00:02:54.380 I don't know. 00:02:54.380 --> 00:02:59.500 Then when Python goes to create these, it goes, oh, they don't want the Dunder PyCache. 00:02:59.500 --> 00:03:01.860 They want it over in this directory over there. 00:03:02.140 --> 00:03:09.700 And so it'll make, you can isolate all of your Dunder PyCache stuff into a separate location on your user profile. 00:03:09.700 --> 00:03:11.720 You can go and just blast that away whenever you feel like. 00:03:11.720 --> 00:03:14.440 But most importantly, it's not within your source code. 00:03:14.440 --> 00:03:17.520 If you like zip it up and hand it out or things like that. 00:03:17.520 --> 00:03:18.120 Oh, wow. 00:03:18.120 --> 00:03:18.580 Okay. 00:03:18.580 --> 00:03:18.780 Yeah. 00:03:18.780 --> 00:03:19.480 That's great. 00:03:19.480 --> 00:03:19.880 Yeah. 00:03:19.880 --> 00:03:30.060 It says, if this is set, Python will write star.pyc files in a mirror directory tree at this path instead of in Dunder PyCache directories within the source tree. 00:03:30.060 --> 00:03:34.780 This is equivalent to specifying the dash x PyCache prefix equals path option. 00:03:34.780 --> 00:03:35.880 So pretty cool, right? 00:03:36.260 --> 00:03:36.440 Yeah. 00:03:36.440 --> 00:03:38.740 I also didn't know about the dash x though either. 00:03:38.740 --> 00:03:39.980 So that's pretty cool. 00:03:39.980 --> 00:03:40.400 Yeah. 00:03:40.400 --> 00:03:42.580 This page that I'm linking to is at the top. 00:03:42.580 --> 00:03:43.820 It has all the command line options. 00:03:43.940 --> 00:03:51.860 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. 00:03:51.860 --> 00:03:54.160 So there's a bunch you can come through here. 00:03:54.160 --> 00:03:56.380 So Python path you can set. 00:03:56.380 --> 00:03:57.640 Python startup. 00:03:57.640 --> 00:04:03.480 So these are the Python commands that I'll execute whenever you start Python, which is kind of interesting. 00:04:03.480 --> 00:04:04.860 Optimize. 00:04:04.860 --> 00:04:06.700 Breakpoint. 00:04:06.700 --> 00:04:07.440 Debug. 00:04:07.440 --> 00:04:08.740 The one we talked about. 00:04:08.860 --> 00:04:13.780 You can set up a hash seed so you can get repeatable deterministic hashing. 00:04:13.780 --> 00:04:15.240 A bunch of stuff is here. 00:04:15.240 --> 00:04:20.700 People can check out how you look at warnings, how much warnings you want to see, whether or not there's buffering. 00:04:20.700 --> 00:04:32.020 So, you know, 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, right? 00:04:32.020 --> 00:04:35.020 Because the buffering got out of, you know, they hit the buffer at different periods. 00:04:36.140 --> 00:04:40.400 So you're going to do things like turn that off, turn on malloc stats if you want to track that. 00:04:40.400 --> 00:04:44.100 So there's like a bunch of things you can come in here and play with. 00:04:44.100 --> 00:04:47.980 But I think the PYC one's an interesting one, especially for people who hand out code. 00:04:47.980 --> 00:04:52.300 You know, like if you're doing like a training or a tutorial and you're like, here, I want to give everybody this. 00:04:52.300 --> 00:04:53.280 You got the PYC. 00:04:53.280 --> 00:05:00.520 I was just looking through there because I'm surprised that you can't specify which XKCD comic to link to. 00:05:00.520 --> 00:05:01.480 I know. 00:05:01.480 --> 00:05:03.820 When I import anti-gravity, which one? 00:05:03.820 --> 00:05:05.580 Don't worry, we'll get there. 00:05:05.580 --> 00:05:07.000 Cool. 00:05:07.000 --> 00:05:07.340 All right. 00:05:07.340 --> 00:05:08.860 Well, that's what I got for this one. 00:05:08.860 --> 00:05:11.640 The Python PYCache prefix. 00:05:11.640 --> 00:05:12.260 Check it out. 00:05:12.260 --> 00:05:12.520 Nice. 00:05:12.520 --> 00:05:13.000 Yeah. 00:05:13.000 --> 00:05:14.960 I wanted to talk about GUIs. 00:05:14.960 --> 00:05:16.460 We haven't talked about GUIs for a while. 00:05:16.460 --> 00:05:18.240 Actually, we haven't. 00:05:18.240 --> 00:05:19.480 We were on such a kick. 00:05:19.480 --> 00:05:20.620 We were on such a kick. 00:05:20.620 --> 00:05:21.180 Yeah. 00:05:21.480 --> 00:05:26.400 But several people have mentioned this to us, so we thought we'd cover it. 00:05:26.400 --> 00:05:28.640 It's a package called NiceGUI. 00:05:28.640 --> 00:05:35.800 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. 00:05:35.800 --> 00:05:38.700 But this is a browser-based thing. 00:05:38.800 --> 00:05:45.840 So this is a package that says it's easy-to-use Python-based UI framework, which shows up in your web browser. 00:05:45.840 --> 00:05:48.600 Buttons, dialogues, markdown, 3D plots, and more. 00:05:48.600 --> 00:05:52.800 What's cool is you can play with it all before you even try it. 00:05:52.800 --> 00:05:55.360 So the documentation is really great. 00:05:56.720 --> 00:06:00.700 And even just here, I thought this was just like a screenshot or something. 00:06:00.700 --> 00:06:02.640 No, you can just like, this is part of it. 00:06:02.640 --> 00:06:06.100 You can move it and interact with it right here in the first page. 00:06:06.100 --> 00:06:06.700 It's pretty cool. 00:06:06.700 --> 00:06:11.000 The full documentation is really pretty great, too. 00:06:11.000 --> 00:06:15.500 I actually want to try to play with this because the code really looks pretty easy to write. 00:06:15.500 --> 00:06:23.360 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? 00:06:23.360 --> 00:06:26.280 And some of these are pretty cool. 00:06:26.280 --> 00:06:27.880 I was playing with text input. 00:06:27.880 --> 00:06:31.180 So it's talking about validation and stuff. 00:06:31.180 --> 00:06:33.860 So you can have some text and you can just start typing. 00:06:33.860 --> 00:06:36.460 And it's saying, oh, that's too long. 00:06:36.460 --> 00:06:40.380 So you can, I mean, this validation is pretty simple of just checking length. 00:06:40.380 --> 00:06:43.900 But you can do all sorts of stuff like email validation or whatever you want it to do. 00:06:43.900 --> 00:06:45.740 Because it's just like a function that's calling. 00:06:45.740 --> 00:06:46.820 So kind of neat. 00:06:46.820 --> 00:06:48.120 You got a validation there. 00:06:48.120 --> 00:06:49.020 Knobs. 00:06:49.020 --> 00:06:49.760 Knobs are fun. 00:06:49.760 --> 00:06:51.440 I was playing with the knobs. 00:06:51.440 --> 00:06:54.060 So just drag knob and turning. 00:06:54.060 --> 00:06:54.720 Anyway. 00:06:55.840 --> 00:07:00.480 I use a context manager to put it into the page with knob. 00:07:00.480 --> 00:07:00.740 What? 00:07:00.740 --> 00:07:02.500 With knob? 00:07:02.500 --> 00:07:04.740 Interesting. 00:07:04.740 --> 00:07:05.480 I wonder why. 00:07:05.480 --> 00:07:06.180 I don't know. 00:07:06.180 --> 00:07:08.480 Oh, to put the icon inside it. 00:07:08.480 --> 00:07:14.940 So you basically, it looks like you're focusing the subsequent commands to be within the container of the knob. 00:07:14.940 --> 00:07:18.940 Because the knob is like a circular progress bar type thing. 00:07:18.940 --> 00:07:21.500 And you can put a volume up icon inside it. 00:07:21.500 --> 00:07:22.400 Okay. 00:07:22.400 --> 00:07:23.560 Anyway. 00:07:23.800 --> 00:07:25.920 Just all sorts of cool stuff. 00:07:25.920 --> 00:07:26.560 Joystick. 00:07:26.560 --> 00:07:27.660 That's really. 00:07:27.660 --> 00:07:29.980 I don't have a joystick to play with this out. 00:07:29.980 --> 00:07:33.280 But some interesting naming there with the joystick. 00:07:33.280 --> 00:07:35.660 But anyway. 00:07:36.440 --> 00:07:38.920 So moving on. 00:07:38.920 --> 00:07:40.440 Date pickers and all sorts of things. 00:07:40.440 --> 00:07:40.780 Ooh. 00:07:40.780 --> 00:07:41.640 That's nice. 00:07:41.640 --> 00:07:42.120 Cool. 00:07:42.120 --> 00:07:43.800 But UI elements. 00:07:43.800 --> 00:07:46.100 If you're okay with trying something new. 00:07:46.100 --> 00:07:49.000 A nice GUI might be right for you. 00:07:49.000 --> 00:07:49.880 So that's it. 00:07:50.260 --> 00:07:50.760 Interesting, isn't it? 00:07:50.760 --> 00:07:56.840 You know, when I look at these types of frameworks that a lot of times I feel like what they say is, 00:07:56.840 --> 00:07:58.500 HTML is terrible. 00:07:58.500 --> 00:07:59.360 The DOM is terrible. 00:07:59.360 --> 00:08:00.420 CSS is terrible. 00:08:00.420 --> 00:08:07.560 Let's create a parallel Python or name your language equivalent where we put elements in the web page. 00:08:07.560 --> 00:08:08.240 I'm like, hmm. 00:08:08.240 --> 00:08:10.180 They may have their drawbacks. 00:08:10.180 --> 00:08:14.920 But at least you have a ton of tools and stuff that apply to HTML and CSS and all those things, right? 00:08:14.920 --> 00:08:15.560 Yeah. 00:08:15.560 --> 00:08:18.700 But with this one, I think there's a lot of cool widgets and stuff that are here. 00:08:18.920 --> 00:08:26.440 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. 00:08:26.440 --> 00:08:37.520 But rather, like, 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? 00:08:37.520 --> 00:08:38.040 Yeah. 00:08:38.040 --> 00:08:44.360 And the places where I would really use something like this are, I mean, this is some short code. 00:08:44.720 --> 00:08:49.260 So especially internal tools or even just stuff for myself. 00:08:49.260 --> 00:08:56.760 If I want to explore some data control like a database or got a bunch of controlling some system or something, 00:08:56.760 --> 00:09:05.260 and I want to quickly throw something together, something like this would be great for just doing a one-pager or something to try it out. 00:09:05.420 --> 00:09:14.340 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, 00:09:14.340 --> 00:09:19.140 and here's how you make it an app that doesn't actually look like a web page and give it to someone. 00:09:19.140 --> 00:09:29.600 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. 00:09:29.600 --> 00:09:30.480 Probably that joystick thing, right? 00:09:30.480 --> 00:09:30.780 Yeah. 00:09:30.780 --> 00:09:32.300 How do you drive your little ribbon around? 00:09:32.300 --> 00:09:39.520 And then one of the nice things that I noticed about the documentation is they've got a bunch of demos. 00:09:39.520 --> 00:09:42.300 Oh, these are the actual demos. 00:09:42.300 --> 00:09:43.980 But there's examples. 00:09:43.980 --> 00:09:45.220 Where did I find those? 00:09:45.220 --> 00:09:46.960 Is it maybe under examples? 00:09:47.180 --> 00:09:49.860 Well, anyway, there's a whole bunch of like actual code. 00:09:49.860 --> 00:09:55.600 So there's repos around that you can try it out with different repositories. 00:09:55.600 --> 00:09:57.140 Maybe it's just on the front page. 00:09:57.140 --> 00:09:59.120 Anyway, I was impressed. 00:09:59.120 --> 00:10:00.100 Oh, yeah, here we go. 00:10:00.100 --> 00:10:02.820 Down at the bottom of the front page, there's like slideshows. 00:10:02.820 --> 00:10:05.700 And even if you want to, will this work with FastAPI? 00:10:05.700 --> 00:10:13.420 Apparently, there's a FastAPI example for just some quick repositories so you can try it out yourself. 00:10:13.420 --> 00:10:16.440 Oh, maybe like an admin page type of dashboard. 00:10:16.440 --> 00:10:18.440 Dashboard thing that you can play with. 00:10:18.440 --> 00:10:21.100 Yeah, there's some OpenCV webcam. 00:10:21.100 --> 00:10:23.260 Infinite scroll for galleries. 00:10:23.260 --> 00:10:36.920 And the amount of like demos of components right there, but then actually specific examples where you can, you know, with the code, with repos that you can just copy and get started with. 00:10:36.920 --> 00:10:40.320 That's pretty impressive that they put all that together right off the bat. 00:10:40.540 --> 00:10:40.720 Yeah. 00:10:40.960 --> 00:10:42.960 So, yeah, it looks like it's definitely worth checking out. 00:10:42.960 --> 00:10:45.320 Do you know what else is worth checking out? 00:10:45.320 --> 00:10:46.620 Our sponsor. 00:10:47.300 --> 00:10:47.700 Yes. 00:10:47.700 --> 00:10:47.740 Yes. 00:10:47.740 --> 00:10:50.260 Microsoft for Startups, Founders Hub. 00:10:50.260 --> 00:10:52.160 Thank you for sponsoring this episode. 00:10:52.660 --> 00:10:58.380 And what was the key that you, how did you generate this code, the text? 00:10:58.380 --> 00:11:06.760 Well, remember, I don't recall who suggested it because I had the American football coach motivational speech version variant last time. 00:11:06.760 --> 00:11:08.920 And somebody said, well, what if it was like Ted Lasso? 00:11:08.920 --> 00:11:11.860 So I said, hey, OpenAI thing. 00:11:11.860 --> 00:11:14.100 Here's the Microsoft ad. 00:11:14.220 --> 00:11:17.600 Could you rewrite it this time in the style of Ted Lasso? 00:11:17.600 --> 00:11:19.040 Yeah. 00:11:19.040 --> 00:11:21.420 So it's an odd episode. 00:11:21.420 --> 00:11:25.440 So I get the honor of trying to be Ted Lasso, which I'm not going to get the voice. 00:11:25.440 --> 00:11:26.180 So apologies. 00:11:26.180 --> 00:11:28.060 And I did not grow up in the Midwest. 00:11:28.060 --> 00:11:30.360 So anyway, let's just get started. 00:11:30.360 --> 00:11:31.400 Hey there, team. 00:11:31.400 --> 00:11:34.980 Gather around because I've got something real special to share with y'all. 00:11:34.980 --> 00:11:39.620 Now, you know how much I believe in the power of teamwork and positivity, right? 00:11:39.620 --> 00:11:46.700 Well, this opportunity I'm telling you about to tell you is just like the perfect assist to your startup goal. 00:11:46.700 --> 00:11:50.040 I'm talking about the Microsoft for startups founders hub, folks. 00:11:50.040 --> 00:11:58.620 Now, imagine, if you will, a locker room full of support for your startup, especially if you're keen on that, their artificial intelligence stuff. 00:11:58.620 --> 00:12:03.340 We're talking over six figures of benefits that will change the game for your team. 00:12:03.340 --> 00:12:06.920 They're offering you 150K in Azure credits. 00:12:07.380 --> 00:12:10.920 And that, my friends, is like having the best player on the field on your side. 00:12:10.920 --> 00:12:22.880 And what's more, the founders hub has given y'all a unique chance to access open AIs, APIs, and the new Azure open API, the Azure open AI service. 00:12:22.880 --> 00:12:29.580 It's like having your own generative AI coach to help you come up with game winning strategies for your applications. 00:12:29.840 --> 00:12:33.920 Now, I know how important it is to have the right support. 00:12:33.920 --> 00:12:42.300 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. 00:12:42.300 --> 00:12:49.160 Plus, you'll be part of a network of mentors who know the startup world like the back of their hand. 00:12:49.160 --> 00:12:53.080 I can't emphasize enough how amazing this opportunity is, friends. 00:12:53.400 --> 00:12:54.000 And guess what? 00:12:54.000 --> 00:12:57.460 It's open to everyone, no matter what stage your startup's at. 00:12:57.460 --> 00:12:58.960 And no funding requirements. 00:12:58.960 --> 00:13:04.280 Just take five minutes to apply, and you'll be on your way to reaping some massive benefits. 00:13:04.280 --> 00:13:05.460 So come on, team. 00:13:05.460 --> 00:13:10.280 Let's harness the power of AI for your startup and join Microsoft for Startup Founders Hub today. 00:13:10.280 --> 00:13:15.760 Head on over to pythonbytes.fm/foundershub 2022 and sign up. 00:13:15.760 --> 00:13:17.640 This is your chance to score big. 00:13:17.740 --> 00:13:19.980 So don't let it slip through your fingers. 00:13:19.980 --> 00:13:26.400 And just so you know, the ad you just heard was written by the same AI you'll get access to. 00:13:26.400 --> 00:13:27.300 Ain't that something? 00:13:27.300 --> 00:13:33.380 So don't wait any longer and sign up at pythonbytes.fm/foundershub 2022. 00:13:33.380 --> 00:13:37.440 A big thank you to Microsoft for supporting this show. 00:13:37.440 --> 00:13:39.480 That open AI, that sure is something. 00:13:39.480 --> 00:13:40.080 All right. 00:13:40.080 --> 00:13:44.040 Speaking of something, let's talk about Ngrok. 00:13:44.040 --> 00:13:45.380 Let's talk about Flask. 00:13:45.380 --> 00:13:46.080 I can't decide. 00:13:46.080 --> 00:13:46.880 Let's talk about both. 00:13:46.880 --> 00:13:50.180 So this one I want to cover is an interesting one. 00:13:50.180 --> 00:13:53.220 So I've talked about Ngrok before. 00:13:53.220 --> 00:13:56.860 For those of you who don't know, like, unfortunately, their website, I don't know what's gone on, 00:13:56.860 --> 00:13:59.360 but they redesigned it in a way they can't really tell what it does. 00:13:59.360 --> 00:14:00.960 But it's just, anyway. 00:14:00.960 --> 00:14:09.800 Ngrok, what it does is it lets you run a command locally and then share your web app, 00:14:09.800 --> 00:14:15.620 whether that be for an API, someone needs to talk to the API, or just the web app itself. 00:14:15.620 --> 00:14:21.660 So for example, Brian, imagine you had created a cool demo of that nice GUI thing. 00:14:21.660 --> 00:14:26.360 And you wanted to let some people, you're in a meeting with your team, like, hey, you guys, y'all should check this out. 00:14:26.360 --> 00:14:27.840 This is really, really cool. 00:14:28.200 --> 00:14:34.260 And what you might do normally would say, well, let's just fire up a screen sharing and I'll drive it around. 00:14:34.260 --> 00:14:37.260 But the interaction part of those widgets is really cool. 00:14:37.260 --> 00:14:39.860 So it'd be better if you could just say and interact with this, right? 00:14:39.860 --> 00:14:47.380 So if you fire up Ngrok, you just give them a URL that reverse SSH tunnels into your machine, 00:14:47.380 --> 00:14:51.700 and then they can access it on the internet with their browser and everyone can play with it live, right? 00:14:51.700 --> 00:14:51.900 Okay. 00:14:51.900 --> 00:14:53.260 So that's really cool. 00:14:53.720 --> 00:15:01.180 I recently used that for, I'm just about to release a course called Python Web Apps that fly with CDNs. 00:15:01.180 --> 00:15:08.380 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? 00:15:08.780 --> 00:15:16.120 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? 00:15:16.120 --> 00:15:17.500 Ngrok is how that happens. 00:15:17.500 --> 00:15:20.840 Same thing with our mobile apps, like, you can see, like, right here. 00:15:20.840 --> 00:15:27.840 We had this problem where some of the data wasn't being passed through as headers correctly to the server. 00:15:27.840 --> 00:15:29.900 And we're like, we cannot figure out why this is not working. 00:15:29.900 --> 00:15:31.340 It's clearly in the headers collection. 00:15:31.340 --> 00:15:33.440 Why is Python not seeing those? 00:15:33.440 --> 00:15:36.940 And it turns out there was like a weird case sensitivity issue or whatever. 00:15:37.320 --> 00:15:41.480 But I just fired up Ngrok, pressed debug on PyCharm and said, all right, try it again. 00:15:41.480 --> 00:15:47.640 And then boom, I'm like stepping through, like on a mobile device, I'm stepping through its interaction with the APIs. 00:15:47.640 --> 00:15:48.360 I'm like, oh, I see. 00:15:48.360 --> 00:15:49.020 Here's what's happening. 00:15:49.020 --> 00:15:49.860 And then we fixed it. 00:15:49.860 --> 00:15:50.340 Super easy. 00:15:50.340 --> 00:15:53.940 All of that is to set up Flask-Ngrok. 00:15:53.940 --> 00:15:56.460 So all of those benefits are awesome. 00:15:56.460 --> 00:16:01.600 But like what I got to do is I have to go fire up Ngrok, go overdo the thing and then come back. 00:16:01.600 --> 00:16:01.860 Right. 00:16:01.940 --> 00:16:09.340 So it'd be cool maybe if I could just press go, either Flask run or just go in PyCharm or VS Code. 00:16:09.340 --> 00:16:15.820 And it would just, in addition to starting up Flask, it would also start up Ngrok, pointing back at whatever the right port is. 00:16:15.820 --> 00:16:16.040 Right. 00:16:16.040 --> 00:16:17.780 So basically that's what this is. 00:16:17.780 --> 00:16:21.760 You just wrap the app in run with Ngrok. 00:16:21.840 --> 00:16:23.640 So you get a run with Ngrok app. 00:16:23.640 --> 00:16:31.420 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. 00:16:31.420 --> 00:16:32.040 Oh, cool. 00:16:32.040 --> 00:16:32.500 Yeah. 00:16:32.500 --> 00:16:38.640 So not a huge, huge feature because sure, you can run Ngrok on your own, but I think it's kind of cool. 00:16:38.640 --> 00:16:52.400 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. 00:16:52.400 --> 00:16:54.540 I want to validate an RSS feed. 00:16:54.540 --> 00:17:06.640 I want to have my API, some API client that is not on my machine like a mobile app or some other, you know, think if this, then that, or one of those types of things. 00:17:06.640 --> 00:17:11.380 All those can just come back right in because you always have this public address available, which I think is pretty cool. 00:17:11.380 --> 00:17:12.200 That's pretty cool. 00:17:12.200 --> 00:17:12.560 Yeah. 00:17:12.560 --> 00:17:15.040 So if that sounds useful, people can check it out. 00:17:15.040 --> 00:17:15.920 Oh, man. 00:17:15.920 --> 00:17:16.320 Okay. 00:17:16.320 --> 00:17:21.500 So I was just thinking, I wonder if random address, because like I need another URL that I'm not doing anything with. 00:17:21.680 --> 00:17:26.220 But I was wondering if random address was taken and random address is taken. 00:17:26.220 --> 00:17:27.340 Is it random addresses? 00:17:27.340 --> 00:17:31.520 No, we have enough. 00:17:31.520 --> 00:17:34.740 I've got several that I'm not using. 00:17:34.740 --> 00:17:36.020 Yeah, that's awesome. 00:17:36.020 --> 00:17:36.900 All right, cool. 00:17:36.900 --> 00:17:40.480 Anyway, people can check this out if they are doing a lot with Ngrok. 00:17:40.480 --> 00:17:47.840 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. 00:17:48.020 --> 00:17:53.980 One of the things you can do is you can set it up so that this random address is repeatable. 00:17:53.980 --> 00:18:00.280 Otherwise, if you just rerun it, you'll get a new random address, which you got to keep typing in by doing like subdomain type things and stuff. 00:18:00.280 --> 00:18:01.320 You've got a paid account. 00:18:01.320 --> 00:18:07.400 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. 00:18:07.760 --> 00:18:09.480 Not completely. 00:18:09.480 --> 00:18:10.560 Just random once. 00:18:10.560 --> 00:18:12.920 Yeah, random once, and let's stick with that for a while. 00:18:12.920 --> 00:18:13.300 All right. 00:18:13.300 --> 00:18:14.940 What do you got for our last one? 00:18:14.940 --> 00:18:17.440 I want to talk about async. 00:18:17.440 --> 00:18:23.240 So Will McCoogan wrote an article called No Async with Python. 00:18:23.240 --> 00:18:24.940 No async, async. 00:18:24.940 --> 00:18:27.540 And that confused me. 00:18:27.800 --> 00:18:29.660 But it's a really well-written article. 00:18:29.660 --> 00:18:39.220 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? 00:18:39.220 --> 00:18:40.460 Or maybe. 00:18:40.460 --> 00:18:44.900 That's what it seems like, at least, to make sure this all works. 00:18:44.900 --> 00:18:50.820 And so that's actually what I guess Textual did at one point is asynced all the things. 00:18:50.820 --> 00:18:54.200 But Textual now is async optional. 00:18:54.200 --> 00:18:57.580 And so this article discusses how they do that. 00:18:57.580 --> 00:19:05.560 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, 00:19:05.560 --> 00:19:10.700 and that callback can, you want it to be either just a normal function or an async function. 00:19:10.700 --> 00:19:17.500 He's utilizing the await me maybe pattern that he borrowed from Simon Willison. 00:19:17.500 --> 00:19:20.060 So Simon Willison wrote about this a couple years ago. 00:19:20.060 --> 00:19:23.920 And he shows, he scrolled almost to the bottom. 00:19:23.920 --> 00:19:29.260 There's this, basically, there's a way to, you have an async function, and it calls. 00:19:29.260 --> 00:19:34.100 So the caller is async, and you're calling something that could be either async or not. 00:19:34.100 --> 00:19:39.480 And you just call something and check to see if it's a coroutine and then await it or don't await it. 00:19:39.480 --> 00:19:48.800 And that's pretty much what Will is showing is, you know, inspect the callback, inspect the result to see if it's awaitable or not. 00:19:48.800 --> 00:19:52.100 Doing it a little bit different method, but similar sort of effect. 00:19:52.100 --> 00:19:53.320 So that's neat. 00:19:53.320 --> 00:19:59.000 So you can provide a mechanism to add a callback that could be async or not optional. 00:19:59.000 --> 00:20:10.240 But the other part around is, is if you're, 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. 00:20:10.320 --> 00:20:17.300 Because sometimes, like, he gives an example of mounting a new widget into Textual. 00:20:17.300 --> 00:20:22.140 The caller might want to care, might care about when that's actually done. 00:20:22.140 --> 00:20:23.520 So they might want to wait for that. 00:20:23.520 --> 00:20:24.520 But they might not. 00:20:24.520 --> 00:20:28.840 They might just, like, keep going because apparently Textual handles it all correctly anyway. 00:20:28.840 --> 00:20:31.000 They won't let something happen. 00:20:31.500 --> 00:20:33.980 But the caller might not care about when it's done. 00:20:33.980 --> 00:20:42.620 So to be able to allow both async and non-async callers to call an async method, that's a little bit yuckier code. 00:20:42.620 --> 00:20:43.860 But he provides it. 00:20:43.860 --> 00:20:48.540 So there's this class, this await mount option. 00:20:48.540 --> 00:20:51.080 So there's, I'm not going to walk through all this code. 00:20:51.180 --> 00:20:53.040 But basically, there's a way to do it. 00:20:53.040 --> 00:20:56.260 And Will has it, has the method to allow you to have. 00:20:56.260 --> 00:21:05.100 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. 00:21:05.100 --> 00:21:07.840 Now, hopefully, this still is kind of ugly. 00:21:07.840 --> 00:21:11.560 So hopefully, as a community, we can come up with a little bit cleaner solution. 00:21:11.560 --> 00:21:13.040 But at least there's a solution. 00:21:13.040 --> 00:21:14.320 So it's kind of nice. 00:21:14.320 --> 00:21:16.760 Yeah, Will did a nice job of this. 00:21:16.760 --> 00:21:23.400 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. 00:21:23.400 --> 00:21:27.360 Because if you're already writing async code, you would like it to be because it's a benefit. 00:21:27.360 --> 00:21:30.020 You can do more in parallel, be more responsive. 00:21:30.020 --> 00:21:38.200 But if it means I have to take my non-async code and now convert the whole thing to know about some parts of it being async, well, that's a hassle. 00:21:38.200 --> 00:21:45.820 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. 00:21:45.820 --> 00:21:49.220 It's like, well, it's so close to awesome, right? 00:21:49.220 --> 00:21:50.340 And much of it is awesome. 00:21:50.340 --> 00:21:56.740 But for example, if I'm in a function and I want to say, here's some async code. 00:21:56.740 --> 00:21:57.780 I want to just run it here. 00:21:57.780 --> 00:22:04.580 So for example, async.io, get event loop, loop.run sort of thing, or just async.io.run to complete. 00:22:04.740 --> 00:22:14.860 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. 00:22:14.860 --> 00:22:19.180 You're like, well, give me just if I don't have one, give me one. 00:22:19.180 --> 00:22:20.740 If there is one, just give me that. 00:22:20.740 --> 00:22:21.100 I don't care. 00:22:21.100 --> 00:22:22.540 I just need to run something async. 00:22:22.620 --> 00:22:25.800 And there's always this weird, I'm not really sure what state I am. 00:22:25.800 --> 00:22:28.300 And if I get it wrong, then it gives me a, it crashes. 00:22:28.300 --> 00:22:29.260 And I was just like, ah. 00:22:30.140 --> 00:22:39.840 So I think that makes it challenging to kind of go, this part we're just going to isolate off the async, which it sounds like Will did here, which is cool. 00:22:39.840 --> 00:22:41.040 So that's really excellent. 00:22:41.040 --> 00:22:41.540 Yeah. 00:22:41.540 --> 00:22:47.280 Really quickly here, too, I've had a couple of shots at this myself as well. 00:22:47.280 --> 00:22:52.720 Nothing like published really very much in terms of what Will's doing or what Simon talked about. 00:22:52.960 --> 00:23:05.520 Like, for example, on FastAPI chameleon and the Jinja equivalent, like let's just put a decorator on top of a FastAPI function and then it returns the HTML view of that and stuff. 00:23:05.520 --> 00:23:10.200 But those FastAPI functions, they can be synchronous or they can be asynchronous. 00:23:10.200 --> 00:23:15.380 And so what it has to do is it has to say, is this, is this function a co-routine? 00:23:15.380 --> 00:23:16.020 Okay. 00:23:16.020 --> 00:23:18.560 The decorator has to return also an async one. 00:23:18.560 --> 00:23:21.760 Otherwise, when you say async and it becomes not async, that's wrong. 00:23:21.760 --> 00:23:23.940 But if it's async one, you can't return the async, right? 00:23:23.940 --> 00:23:26.920 Like, so you've got to juggle this a lot, which is kind of a pain. 00:23:26.920 --> 00:23:41.980 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. 00:23:41.980 --> 00:23:43.740 So, yeah, it's interesting. 00:23:43.740 --> 00:23:46.120 But, yeah, that's a cool article. 00:23:46.120 --> 00:23:46.720 Yeah. 00:23:46.720 --> 00:23:50.000 And do you remember the Call Me Maybe song? 00:23:50.000 --> 00:23:51.280 Call Me Maybe. 00:23:51.520 --> 00:23:52.060 Yeah, I do. 00:23:52.060 --> 00:23:52.300 Yeah. 00:23:52.300 --> 00:23:53.020 Await Me Maybe. 00:23:53.020 --> 00:23:57.660 So Chris May added, hey, I just defined you and this may be async. 00:23:57.660 --> 00:24:00.580 But here's my variable, Await Me Maybe. 00:24:00.580 --> 00:24:01.400 Nice. 00:24:01.400 --> 00:24:02.980 Oh, well done, Chris. 00:24:02.980 --> 00:24:04.760 Well done. 00:24:04.760 --> 00:24:06.340 I love it. 00:24:06.340 --> 00:24:06.880 I love it. 00:24:06.880 --> 00:24:08.820 Well, I don't have any extras, Brian. 00:24:08.820 --> 00:24:09.420 You got any? 00:24:09.420 --> 00:24:10.440 I do. 00:24:10.440 --> 00:24:12.120 We'll try to make them quick, though. 00:24:12.120 --> 00:24:13.940 So PyPI has a blog now. 00:24:13.940 --> 00:24:15.920 Oh, let's pop over to here. 00:24:15.920 --> 00:24:17.300 PyPI has a blog now. 00:24:17.500 --> 00:24:18.980 So anyway, go check it out. 00:24:18.980 --> 00:24:20.600 There's a welcome article. 00:24:20.600 --> 00:24:22.700 So that's nice. 00:24:22.700 --> 00:24:23.820 Neat. 00:24:23.820 --> 00:24:26.620 And then, OK, so another extra. 00:24:26.620 --> 00:24:27.740 Apparently, Docker. 00:24:27.740 --> 00:24:36.860 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. 00:24:37.720 --> 00:24:40.920 But, you know, anyway, so that's cool. 00:24:40.920 --> 00:24:42.500 Maybe I won't have to pay next year. 00:24:42.500 --> 00:24:46.040 I guess they're offering refunds or something, but I'll look into it. 00:24:46.040 --> 00:24:47.440 I guess there must have been a big backlash. 00:24:47.440 --> 00:24:48.660 I haven't been tracking this, but. 00:24:48.660 --> 00:24:49.320 Oh, yeah. 00:24:49.320 --> 00:24:57.820 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. 00:24:57.820 --> 00:25:03.160 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. 00:25:03.160 --> 00:25:04.220 And yeah. 00:25:04.220 --> 00:25:05.800 Or if you want to use it without. 00:25:05.800 --> 00:25:09.540 So if you want to use it without the user interface, you can use it for free. 00:25:09.540 --> 00:25:13.360 But if you like people asking, are are you using it? 00:25:13.360 --> 00:25:14.380 Yes, we are. 00:25:14.380 --> 00:25:15.400 Because we debug with it. 00:25:15.400 --> 00:25:16.840 So. 00:25:16.840 --> 00:25:18.620 So I'm glad they're backing off. 00:25:18.620 --> 00:25:19.460 I still want to. 00:25:19.460 --> 00:25:21.460 I mean, of course, it's a great service. 00:25:21.460 --> 00:25:23.280 They should be able to make money somehow. 00:25:23.860 --> 00:25:25.540 But there should be it. 00:25:25.540 --> 00:25:26.260 So it's good news. 00:25:26.260 --> 00:25:31.220 I only the only thing left is I have is a joke. 00:25:31.220 --> 00:25:33.660 Do you want to do mine first or yours? 00:25:33.660 --> 00:25:35.240 Let's hear yours first. 00:25:35.240 --> 00:25:35.620 Let's go. 00:25:35.620 --> 00:25:37.360 It's just sort of. 00:25:37.360 --> 00:25:38.320 I was looking up. 00:25:38.320 --> 00:25:44.180 I was looking up some documentation for pytest Cove and noticed at the bottom. 00:25:44.180 --> 00:25:45.460 So there's a there's mark. 00:25:45.460 --> 00:25:49.140 There's it provides a no cover marker, which is nice. 00:25:49.140 --> 00:25:50.920 So you can say don't cover this test. 00:25:50.920 --> 00:25:53.220 And then there's a fixture. 00:25:53.520 --> 00:25:55.040 You can also use that as a fixture. 00:25:55.040 --> 00:25:57.200 But then there's also the no cover fixture. 00:25:57.200 --> 00:26:00.800 But there's a Cove fixture, which why would you use that? 00:26:00.800 --> 00:26:08.100 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. 00:26:08.100 --> 00:26:12.300 Some say this is disguised as a foot gun and should be removed. 00:26:12.300 --> 00:26:17.380 And some things some think mysteries make life more interesting and it should be left alone. 00:26:17.380 --> 00:26:20.720 I love finding stuff like that in documentation. 00:26:20.720 --> 00:26:23.380 Some think mysteries make life more interesting. 00:26:23.380 --> 00:26:23.880 Indeed. 00:26:23.880 --> 00:26:25.080 Indeed they do. 00:26:25.080 --> 00:26:26.000 Okay. 00:26:26.000 --> 00:26:27.200 Fantastic. 00:26:27.200 --> 00:26:27.420 All right. 00:26:27.420 --> 00:26:29.280 I got a quick one for you as well. 00:26:29.280 --> 00:26:32.120 This one, you knew an XKCD was coming. 00:26:32.120 --> 00:26:33.700 Good reference earlier. 00:26:33.900 --> 00:26:44.840 So this has to do with like 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. 00:26:44.840 --> 00:26:48.420 So there's two parallel universes here. 00:26:48.640 --> 00:26:51.820 On one, this woman just wrote this code. 00:26:51.820 --> 00:26:57.120 It took some extra work to build, but now we're able to use it for all of our future projects. 00:26:57.120 --> 00:27:00.380 And the caption for that is, how to ensure your code is never used? 00:27:02.380 --> 00:27:02.680 Yes. 00:27:02.680 --> 00:27:06.480 And then the other alternate world is, let's not overthink it. 00:27:06.480 --> 00:27:10.580 If this code is still in use that far in the future, it will have bigger problems. 00:27:10.580 --> 00:27:12.120 How to ensure that your code lives forever? 00:27:12.120 --> 00:27:13.720 Yeah. 00:27:13.720 --> 00:27:20.220 And the hover is surely no one, everyone will recognize how flexible and useful this architecture is. 00:27:20.220 --> 00:27:24.840 Spend a huge amount of effort painstakingly preserving and updating this garbage I wrote in 20 minutes. 00:27:24.840 --> 00:27:26.880 Yeah. 00:27:26.880 --> 00:27:30.080 Well, I mean, there's so many examples of that, isn't there? 00:27:30.080 --> 00:27:30.460 I mean. 00:27:30.460 --> 00:27:30.900 Oh, yeah. 00:27:30.900 --> 00:27:31.980 Yeah. 00:27:31.980 --> 00:27:34.340 I mean, internally, there's tons. 00:27:34.340 --> 00:27:38.160 There's like, oh, just throw this thing together, build script or whatever. 00:27:38.160 --> 00:27:40.360 And it's just, we'll rewrite it later. 00:27:40.360 --> 00:27:42.360 10 years later, we haven't rewritten it. 00:27:42.360 --> 00:27:43.560 Things like that. 00:27:44.140 --> 00:27:47.920 I mean, Flask was like a really quick hack, wasn't it? 00:27:47.920 --> 00:27:49.700 Like a joke at first or something? 00:27:49.700 --> 00:27:51.420 I think it was an April Fool's thing. 00:27:51.420 --> 00:27:51.660 Yeah. 00:27:51.660 --> 00:27:52.280 I think so. 00:27:52.280 --> 00:27:53.920 Anyway. 00:27:53.920 --> 00:28:02.520 And the other side is the lesson that I think people should learn is planning on reuse is just a mistake. 00:28:02.520 --> 00:28:12.000 And I've been in many, many design meetings where it's like, let's not plan six years out into the future on this. 00:28:12.000 --> 00:28:12.900 That's ridiculous. 00:28:13.100 --> 00:28:13.840 We don't even know. 00:28:13.840 --> 00:28:16.460 Because I've also seen people plan for it. 00:28:16.460 --> 00:28:17.440 And it is reused. 00:28:17.440 --> 00:28:18.420 And it is maintained. 00:28:18.740 --> 00:28:24.680 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. 00:28:24.680 --> 00:28:25.660 It's something else. 00:28:25.660 --> 00:28:26.340 Yeah. 00:28:26.340 --> 00:28:30.100 Somewhere in the middle there lives a, let's not overthink this, get it out there. 00:28:30.100 --> 00:28:32.120 Oh, let's take a moment and refactor it. 00:28:32.340 --> 00:28:35.320 So it's like more reasonable in the way we now know it needs to be. 00:28:35.320 --> 00:28:36.000 Carry on. 00:28:36.000 --> 00:28:39.360 So my advice, keep the interface simple. 00:28:39.360 --> 00:28:40.280 Keep it minimal. 00:28:40.280 --> 00:28:41.880 Document it and test it. 00:28:41.880 --> 00:28:43.800 And then if it grows, great. 00:28:43.800 --> 00:28:44.520 Yeah. 00:28:44.520 --> 00:28:45.100 Excellent. 00:28:45.100 --> 00:28:45.880 Thanks for that. 00:28:45.880 --> 00:28:46.300 That's funny. 00:28:46.300 --> 00:28:47.500 Yeah, for sure. 00:28:47.900 --> 00:28:49.440 And thanks everybody for showing up. 00:28:49.440 --> 00:28:51.480 And thanks Michael again for showing up.