Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to your earbuds.
This is episode 329, recorded March 30th, 2023.
And I'm Brian Okken.
I'm Michael Kennedy.
And before we get started, I want to do a couple things.
I want to thank Microsoft First Startup Founders Hub for sponsoring this episode.
Please listen to their spot.
We've got a very special random guest appearance or voice for that ad read.
So that'll be fun.
I also, if you're listening to this, I'd like to encourage you to, and you've never watched the live show,
I'd like to encourage you to watch our live show on YouTube or streaming.
Today is Thursday, but we usually record Tuesday at noon or 11.
No, Tuesday at 11.
Yeah, I should have practiced this.
Anyway, but welcome.
And the people that are here today are watching on YouTube.
Thanks for watching.
And why don't you kick us off, Michael?
I got something.
I think it'll resonate with you folks.
Maybe you already are aware of this.
This is news to me.
Comes to us from Brandon Hannigan.
So thanks for sending that in.
And it's an environment variable.
And in and of itself, it's interesting.
That it leads to many more environment variables you can play with is also pretty awesome.
So this one is about the Dunder PyCache folder.
So I don't know how you feel about these, Brian.
I'm glad they exist.
I'm not necessarily super glad that they're spread out a bunch of random folders.
So like a lot of my projects have many different modules and different folders, you know, submodule type setup.
And when I run them, they all get filled up with Dunder PyCache folders with startup PYC, you know, compiled Python.
Like people might think, I think Python is compiled, but yes, Python is compiled.
It's just then interpreted, not, you know, all the way to machine instructions, right?
So those files end up in the Dunder PyCache, which spread throughout your project structure.
Sometimes I want to make a copy of that.
I want to move that around.
I want to 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, you know, recursively, which is not at 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?
You know what I mean?
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?
Yeah.
I mean, it's better than it used to be of having the PYCs right next to the Python files.
Yes.
It is an improvement.
Yeah.
But this thing that Brendan said in, you can set the Python PyCache prefix, which is a path.
Maybe it should be called Python Cache folder.
I don't know.
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.
If you want it to be a one-off type thing.
I don't know.
Then when Python goes to create these, it goes, oh, they don't want the Dunder PyCache.
They want it over in this directory over there.
And so it'll make, you can isolate all of your Dunder PyCache stuff into a separate location on your user profile.
You can go and just blast that away whenever you feel like.
But most importantly, it's not within your source code.
If you like zip it up and hand it out or things like that.
Oh, wow.
Okay.
Yeah.
That's great.
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 PyCache directories within the source tree.
This is equivalent to specifying the dash x PyCache prefix equals path option.
So pretty cool, right?
Yeah.
I also didn't know about the dash x though either.
So that's pretty cool.
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.
So there's a bunch you can come through here.
So Python path you can set.
Python startup.
So these are the Python commands that I'll execute whenever you start Python, which is kind of interesting.
Optimize.
Breakpoint.
Debug.
The one we talked about.
You can set up a hash seed so you can get repeatable deterministic hashing.
A bunch of stuff is here.
People can check out how you look at warnings, how much warnings you want to see, whether or not there's buffering.
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?
Because the buffering got out of, you know, they hit the buffer at different periods.
So you're going to do things like turn that off, turn on malloc stats if you want to track that.
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.
You know, like if you're doing like a training or a tutorial and you're like, here, I want to give everybody this.
You got the PYC.
I was just looking through there because I'm surprised that you can't specify which XKCD comic to link to.
I know.
When I import anti-gravity, which one?
Don't worry, we'll get there.
Cool.
All right.
Well, that's what I got for this one.
The Python PYCache prefix.
Check it out.
Nice.
Yeah.
I wanted to talk about GUIs.
We haven't talked about GUIs for a while.
Actually, we haven't.
We were on such a kick.
Yeah.
But several people have mentioned this to us, so we thought we'd cover it.
It's a package called NiceGUI.
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.
But this is a browser-based thing.
So this is a package that says it's easy-to-use Python-based UI framework, which shows up in your web browser.
Buttons, dialogues, markdown, 3D plots, and more.
What's cool is you can play with it all before you even try it.
So the documentation is really great.
And even just here, I thought this was just like a screenshot or something.
No, you can just like, this is part of it.
You can move it and interact with it right here in the first page.
It's pretty cool.
The full documentation is really pretty great, too.
I actually want to try to play with this because the code really looks pretty easy to write.
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 and 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 whatever you want it to do.
Because it's just like a function that's calling.
So kind of neat.
You got a validation there.
Knobs.
Knobs are fun.
I was playing with the knobs.
So just drag knob and turning.
Anyway.
I use a context manager to put it into the page with knob.
What?
With knob?
Interesting.
I wonder why.
I don't know.
Oh, to put the icon inside it.
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 circular progress bar type thing.
And you can put a volume up icon inside it.
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.
Ooh.
That's nice.
Cool.
But UI elements.
If you're okay with trying something new.
A nice GUI might be right for you.
So that's it.
Interesting, isn't it?
You know, when I look at these types of frameworks that a lot of times I feel like what they say is,
HTML is terrible.
The DOM is terrible.
CSS is terrible.
Let's create a parallel Python or name your language equivalent where we put elements in the web page.
I'm like, hmm.
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?
Yeah.
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, 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?
Yeah.
And the places where I would really use something like this are, I mean, this is some short code.
So especially internal tools or even just stuff for myself.
If I want to explore some data control like a database or got a bunch of controlling some system or something,
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.
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.
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?
Yeah.
How do you drive your little ribbon around?
And then one of the nice things that I noticed about the documentation is they've got a bunch of demos.
Oh, these are the actual demos.
But there's examples.
Where did I find those?
Is it maybe under examples?
Well, anyway, there's a whole bunch of like actual code.
So there's repos around that you can try it out with different repositories.
Maybe it's just on the front page.
Anyway, I was impressed.
Oh, yeah, here we go.
Down at the bottom of the front page, there's like slideshows.
And even if you want to, will this work with FastAPI?
Apparently, there's a FastAPI example for just some quick repositories so you can try it out yourself.
Oh, maybe like an admin page type of dashboard.
Dashboard thing that you can play with.
Yeah, there's some OpenCV webcam.
Infinite scroll for galleries.
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.
That's pretty impressive that they put all that together right off the bat.
Yeah.
So, yeah, it looks like it's definitely worth checking out.
Do you know what else is worth checking out?
Our sponsor.
Yes.
Microsoft for Startups, Founders Hub.
Thank you for sponsoring this episode.
And what was the key that you, how did you generate this code, the text?
Well, remember, I don't recall who suggested it because I had the American football coach motivational speech version variant last time.
And somebody said, well, what if it was like Ted Lasso?
So I said, hey, OpenAI thing.
Here's the Microsoft ad.
Could you rewrite it this time in the style of Ted Lasso?
Yeah.
So it's an odd episode.
So I get the honor of trying to be Ted Lasso, which I'm not going to get the voice.
So apologies.
And I did not grow up in the Midwest.
So anyway, let's just get started.
Hey there, team.
Gather around because I've got something real special to share with y'all.
Now, you know how much I believe in the power of teamwork and positivity, right?
Well, this opportunity I'm telling you about to tell you is just like the perfect assist to your startup goal.
I'm talking about the Microsoft for startups founders hub, folks.
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.
We're talking over six figures of benefits that will change the game for your team.
They're offering you 150K in Azure credits.
And that, my friends, is like having the best player on the field on your side.
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.
It's like having your own generative AI coach to help you come up with game winning strategies for your applications.
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.
Plus, you'll be part of a network of mentors who know the startup world like the back of their hand.
I can't emphasize enough how amazing this opportunity is, friends.
And guess what?
It's open to everyone, no matter what stage your startup's at.
And no funding requirements.
Just take five minutes to apply, and you'll be on your way to reaping some massive benefits.
So come on, team.
Let's harness the power of AI for your startup and join Microsoft for Startup Founders Hub today.
Head on over to pythonbytes.fm/foundershub 2022 and sign up.
This is your chance to score big.
So don't let it slip through your fingers.
And just so you know, the ad you just heard was written by the same AI you'll get access to.
Ain't that something?
So don't wait any longer and sign up at pythonbytes.fm/foundershub 2022.
A big thank you to Microsoft for supporting this show.
That open AI, that sure is something.
All right.
Speaking of something, let's talk about Ngrok.
Let's talk about Flask.
I can't decide.
Let's talk about both.
So this one I want to cover is an interesting one.
So I've talked about Ngrok before.
For those of you who don't know, like, unfortunately, their website, I don't know what's gone on,
but they redesigned it in a way they can't really tell what it does.
But it's just, anyway.
Ngrok, 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.
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, like, hey, you guys, y'all should check this out.
This is really, really cool.
And 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.
So it'd be better if you could just say and interact with this, right?
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, right?
Okay.
So that's really cool.
I recently used that for, I'm just about to release a course called Python Web Apps that fly with CDNs.
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?
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?
Ngrok is how that happens.
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.
It's clearly in the headers collection.
Why is Python not seeing those?
And it turns out there was like a weird case sensitivity issue or whatever.
But I just fired up Ngrok, pressed debug on PyCharm and said, all right, try it again.
And then boom, I'm like stepping through, like on a mobile device, I'm stepping through its interaction with the APIs.
I'm like, oh, I see.
Here's what's happening.
And then we fixed it.
Super easy.
All of that is to set up Flask-Ngrok.
So all of those benefits are awesome.
But like what I got to do is I have to go fire up Ngrok, go overdo the thing and then come back.
Right.
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.
So basically that's what this is.
You just wrap the app in run with Ngrok.
So you get a run with Ngrok app.
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.
Oh, cool.
Yeah.
So not a huge, huge feature because sure, you can run Ngrok on your own, but I think it's kind of cool.
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.
I want to validate an RSS feed.
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.
All those can just come back right in because you always have this public address available, which I think is pretty cool.
That's pretty cool.
Yeah.
So if that sounds useful, people can check it out.
Oh, man.
Okay.
So I was just thinking, I wonder if random address, because 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.
Is it random addresses?
No, we have enough.
I've got several that I'm not using.
Yeah, that's awesome.
All right, cool.
Anyway, people can check this out if they are doing a lot with Ngrok.
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.
One of the things you can do is you can set it up so that this random address is repeatable.
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.
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.
Not completely.
Just random once.
Yeah, random once, and let's stick with that for a while.
All right.
What do you got for our last one?
I want to talk about async.
So Will McCoogan wrote an article called No Async with Python.
No async, async.
And that confused me.
But it's a really well-written article.
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?
Or maybe.
That's what it seems like, at least, to make sure this all works.
And so that's actually what I guess Textual did at one point is asynced all the things.
But Textual now is async optional.
And so this article discusses how they do that.
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.
So Simon Willison wrote about this a couple years ago.
And he shows, he scrolled almost to the bottom.
There's this, basically, there's a way to, you have an async function, and it calls.
So the caller is async, and you're calling something that could be either async or not.
And you just call something and check to see if it's a coroutine and then await it or don't await it.
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.
Doing it a little bit different method, but similar sort of effect.
So that's neat.
So you can provide a mechanism to add a callback that could be async or not optional.
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.
Because sometimes, like, he gives an example of mounting a new widget into Textual.
The caller might want to care, might care about when that's actually done.
So they might want to wait for that.
But they might not.
They might just, like, keep going because apparently Textual handles it all correctly anyway.
They won't let something happen.
But the caller might not care about when it's done.
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.
So there's this class, this await mount option.
So there's, I'm not going to 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.
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.
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.
So it's kind of nice.
Yeah, Will did a nice job of this.
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.
Because if you're already writing async code, you would like it to be because it's a benefit.
You can do more in parallel, be more responsive.
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.
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.
It's like, well, it's so close to awesome, right?
And much of it is awesome.
But for example, if I'm in a function and I want to say, here's some async code.
I want to just run it here.
So for example, async.io, get event loop, loop.run sort of thing, or just async.io.run to complete.
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 just if I don't have one, give me one.
If there is one, just give me that.
I don't care.
I just need to run something async.
And there's always this weird, I'm not really sure what state I am.
And if I get it wrong, then it gives me a, it crashes.
And I was just like, ah.
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.
So that's really excellent.
Yeah.
Really quickly here, too, I've had a couple of shots at this myself as well.
Nothing like published really very much in terms of what Will's doing or what Simon talked about.
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.
But those FastAPI functions, they can be synchronous or they can be asynchronous.
And so what it has to do is it has to say, is this, is this function a co-routine?
Okay.
The decorator has to return also an async one.
Otherwise, when you say async and it becomes not async, that's wrong.
But if it's async one, you can't return the async, right?
Like, so you've got to juggle this a lot, which is kind of a pain.
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.
So, yeah, it's interesting.
But, yeah, that's a cool article.
Yeah.
And do you remember the Call Me Maybe song?
Call Me Maybe.
Yeah, I do.
Yeah.
Await Me Maybe.
So Chris May added, hey, I just defined you and this may be async.
But here's my variable, Await Me Maybe.
Nice.
Oh, well done, Chris.
Well done.
I love it.
Well, I don't have any extras, Brian.
You got any?
I do.
We'll try to make them quick, though.
So PyPI has a blog now.
Oh, let's pop over to here.
PyPI has a blog now.
So anyway, go check it out.
There's a welcome article.
So that's nice.
Neat.
And then, OK, so another extra.
Apparently, Docker.
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.
But, you know, anyway, so that's cool.
Maybe I won't have to pay next year.
I guess they're offering refunds or something, but I'll look into it.
I guess there must have been a big backlash.
I haven't been tracking this, but.
Oh, yeah.
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 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.
So.
So I'm glad they're backing off.
I still want to.
I mean, of course, it's a great service.
They should be able to make money somehow.
But there should be it.
So it's good news.
I only the only thing left is I have is a joke.
Do you want to do mine first or yours?
Let's hear yours first.
Let's go.
It's just sort of.
I was looking up.
I was looking up some documentation for pytest Cove and noticed at the bottom.
So there's a there's mark.
There's it provides a no cover marker, which is nice.
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.
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.
I love finding stuff like that in documentation.
Some think mysteries make life more interesting.
Indeed.
Indeed they do.
Okay.
Fantastic.
All right.
I got a quick one for you as well.
This one, you knew an XKCD was coming.
Good reference earlier.
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.
So there's two parallel universes here.
On one, this woman just wrote this code.
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 used?
Yes.
And then the other alternate world is, let's not overthink it.
If this code is still in use that far in the future, it will have bigger problems.
How to ensure that your code lives forever?
Yeah.
And the hover is surely no one, everyone will recognize how flexible and useful this architecture is.
Spend a huge amount of effort painstakingly preserving and updating this garbage I wrote in 20 minutes.
Yeah.
Well, I mean, there's so many examples of that, isn't there?
I mean.
Oh, yeah.
Yeah.
I mean, internally, there's tons.
There's like, oh, just throw this thing together, build script or whatever.
And it's just, we'll rewrite it later.
10 years later, we haven't rewritten it.
Things like that.
I mean, Flask was like a really quick hack, wasn't it?
Like a joke at first or something?
I think it was an April Fool's thing.
Yeah.
I think so.
Anyway.
And the other side is the lesson that I think people should learn is planning on reuse is just a mistake.
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.
That's ridiculous.
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.
It's something else.
Yeah.
Somewhere in the middle there lives a, let's not overthink this, get it out there.
Oh, let's take a moment and refactor it.
So it's like more reasonable in the way we now know it needs to be.
Carry on.
So my advice, keep the interface simple.
Keep it minimal.
Document it and test it.
And then if it grows, great.
Yeah.
Excellent.
Thanks for that.
That's funny.
Yeah, for sure.
And thanks everybody for showing up.
And thanks Michael again for showing up.