WEBVTT

00:00:00.001 --> 00:00:04.520
Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to

00:00:04.520 --> 00:00:12.120
your earbuds. This is episode 184, recorded May 27th, 2020. I'm Brian Okken. And I'm Michael

00:00:12.120 --> 00:00:17.280
Kennedy. And this episode is sponsored by DigitalOcean. Thank you, DigitalOcean. Yeah,

00:00:17.280 --> 00:00:21.160
thanks, DigitalOcean. Really appreciate it. Well, I'm waiting, Michael. You're waiting?

00:00:21.160 --> 00:00:24.940
What are you waiting for? I'm waiting for the next AsyncIO story.

00:00:24.940 --> 00:00:29.940
I guess it's my turn, isn't it? Okay. Sorry, let me see if I can get this right.

00:00:30.180 --> 00:00:37.080
So the topic I want to talk about is waiting in AsyncIO. Yeah, so the magic of AsyncIO,

00:00:37.080 --> 00:00:44.180
which was introduced in Python 3.4, never really appeared until Python 3.5 when the async and await

00:00:44.180 --> 00:00:50.920
keywords came into being, which let you write code that looks like standard single-threaded serial code,

00:00:50.920 --> 00:00:57.460
but actually is multi-threaded, or at least parallel concurrent to some degree. Depends on

00:00:57.460 --> 00:01:02.240
how you're running it, whether it's truly multi-threaded. Anyway, there's a lot of options,

00:01:02.240 --> 00:01:10.440
let's say, on how you can interact with these co-routines and these tasks that are generated by the AsyncIO

00:01:10.440 --> 00:01:16.700
framework. And anytime there's like four ways to do something in programming, you should be asking

00:01:16.700 --> 00:01:24.860
yourself, one, why are there four ways to do this? But more importantly, when does way one apply best?

00:01:25.020 --> 00:01:29.500
And what scenario should I use way three? And what is the trade-off between two and four?

00:01:29.500 --> 00:01:37.980
And so on, right? So that's the case with AsyncIO. There's tons of ways to wait or await things.

00:01:37.980 --> 00:01:42.580
And Hank, get the pronunciation right for me. I know you got it.

00:01:42.580 --> 00:01:43.700
You got it. That's good.

00:01:43.700 --> 00:01:47.860
Sorry.

00:01:47.860 --> 00:01:50.400
That's just a running thing. I can never get it right.

00:01:50.400 --> 00:01:56.060
No. Well, the problem is, I think I had it right. And then we went back and forth so many times with so

00:01:56.060 --> 00:02:02.340
many variations. Now it's broken. Sorry about messing up your name there. So I wrote a great article,

00:02:02.340 --> 00:02:07.160
though, called Waiting in AsyncIO that does exactly that, that says, here are all the ways,

00:02:07.160 --> 00:02:11.380
here's the pluses, and here's the minuses, and the situations in which you apply them.

00:02:11.440 --> 00:02:18.460
So if you're serious about using AsyncIO and you're building real things, basically this podcast

00:02:18.460 --> 00:02:22.800
episode is for you because I have this and another one that bring two really cool ideas together.

00:02:22.800 --> 00:02:25.900
But let's talk about the waiting one first.

00:02:26.460 --> 00:02:33.940
So it's really easy to start doing some work, right? I can have two coroutines, let's say F and G,

00:02:33.940 --> 00:02:40.620
and I could say I want the result of F by saying, you know, result equals await calling F,

00:02:40.620 --> 00:02:47.280
and then result G equals await calling G. And that's fine if what you're looking for is more

00:02:47.280 --> 00:02:53.880
concurrent execution of that part of code, right? So this is, say, in a web method, like a view,

00:02:54.280 --> 00:02:59.280
someone makes a request, and there's not a lot you can do to make things go faster,

00:02:59.280 --> 00:03:03.260
potentially for that one request, but you can say, let the server be less busy,

00:03:03.260 --> 00:03:08.440
so it could handle like 10 or 20 times more of the same request, right? So this real simple,

00:03:08.440 --> 00:03:14.360
like just await calling these functions, these Async functions, it will allow your system to scale

00:03:14.360 --> 00:03:19.940
more, but it won't make things faster. Like for example, if you're trying to crawl 20 web pages,

00:03:19.940 --> 00:03:24.260
this will not make it any faster, it'll just make your code more complicated. So don't do that.

00:03:24.260 --> 00:03:28.740
So there's other ways which you want to do that. Another thing that I think a lot of people

00:03:28.740 --> 00:03:34.900
don't quite get is when you call one of these Async functions, like Async Def Function Name,

00:03:34.900 --> 00:03:43.040
when you call it, it doesn't actually start it until you either await it or create a task from it.

00:03:43.500 --> 00:03:49.780
So if you call like F and then G, and you think you're going to come back and get to them later,

00:03:49.780 --> 00:03:54.440
now they're running. No, actually, they're not unless you've created a task, which starts them.

00:03:54.440 --> 00:03:59.040
So you either have to await them, which blocks, or create these tasks to like kick them off.

00:03:59.040 --> 00:04:02.600
That's pretty interesting, right? Like that's not super obvious. I think normally when you call a

00:04:02.600 --> 00:04:09.100
function, it does a thing. But here, not so much. So some other options, if you could call them both as

00:04:09.100 --> 00:04:13.040
create them as tasks, and then you could await those tasks, right? Because their tasks are already

00:04:13.040 --> 00:04:17.340
running, and then you await them both, whichever one first finishes first doesn't matter. You wait

00:04:17.340 --> 00:04:21.580
till the first one's done, and maybe the second one's already done. So that's probably the pattern

00:04:21.580 --> 00:04:27.760
that most people are going to be using. But you can also use Async IO Gather that takes one or more

00:04:27.760 --> 00:04:33.540
awaitables as a star args, and then it waits for them all to finish, which is pretty cool. And that

00:04:33.540 --> 00:04:39.640
itself is a future thing that you can await, right? So you would say await Async IO dot gather.

00:04:39.640 --> 00:04:40.960
Oh, that's cool.

00:04:40.960 --> 00:04:44.880
Yeah, gather is awesome, because I can create all these tasks. And I just say, I just need them all

00:04:44.880 --> 00:04:49.060
to be done. And when that's done, we can get the results and carry on. And what's cool is when you

00:04:49.060 --> 00:04:55.860
await gather, you get a tuple of results. So if I say Async IO gather function one or task one,

00:04:55.860 --> 00:05:02.660
task two, then it returns the result one comma result two as a tuple. So you can gather them up and get

00:05:02.660 --> 00:05:06.740
all the answers back, which is pretty cool. Yeah, that's really neat. Yeah, one of the problems

00:05:06.740 --> 00:05:12.160
with gather, though, is you're saying I'm willing to wait forever for this to finish. And sometimes

00:05:12.160 --> 00:05:18.620
that's fine. But sometimes things don't return correctly or ever or in the right amount of time.

00:05:18.620 --> 00:05:24.480
So you can use wait for, which is nice and allows you to pass a timeout. But what's a little bit

00:05:24.480 --> 00:05:30.840
better than a wait for is there's an Async timeout package on PyPI, which I'd not heard of. And you can

00:05:30.840 --> 00:05:36.600
basically create a block that will a width block that will timeout. So you can say I want to have

00:05:36.600 --> 00:05:42.060
an Async with timeout five seconds and then do a whole bunch of function calls and awaiting and all

00:05:42.060 --> 00:05:47.420
that. And either they're all going to finish or when five seconds passes, everything gets canceled

00:05:47.420 --> 00:05:51.220
that hasn't finished. That's cool. That's pretty cool, right? Another one that's really interesting

00:05:51.220 --> 00:05:56.940
is I start a bunch of work. And then I would like to say I kick off, I'm doing web scraping,

00:05:56.940 --> 00:06:01.920
and I want to go get the results of 20 web pages. I kick off 20 requests and then I want

00:06:01.920 --> 00:06:06.280
to process them as they complete. Like the first one that's done, I want to work on that. Then

00:06:06.280 --> 00:06:14.860
the next one, then the next one. So you can create a task or an iterable rather from saying AsyncIO.as

00:06:14.860 --> 00:06:19.820
completed and you give it a bunch of tasks and it gives you an iterator that you can for in

00:06:19.820 --> 00:06:25.000
over that gives you the first completed one, then the second completed one and just blocks until

00:06:25.000 --> 00:06:32.100
the next one is completed. So you kick a bunch off and then you just say for completed tasks in AsyncIO.as

00:06:32.100 --> 00:06:35.580
completed and you give it your running tasks. That's really slick, isn't it?

00:06:35.580 --> 00:06:39.440
That is slick. Looks like it has a timeout also that you can add to it.

00:06:39.440 --> 00:06:42.620
Yeah, very nice. Yep. You can give it a timeout indeed.

00:06:42.620 --> 00:06:48.400
Now, there's a few more things covered in there and I didn't go over the trade-offs too much.

00:06:48.400 --> 00:06:52.560
You know, here's the scenario where you use this and that. So if you really care about this,

00:06:52.560 --> 00:06:58.060
two things to do. One is check out the article. It's got a lot of details and each subsection has

00:06:58.060 --> 00:07:02.020
a little trade-offs. Here's the good, here's the bad, which is nice. And also you can check out my

00:07:02.020 --> 00:07:07.200
Async course, which talks about this and a whole bunch of other things on Async as well. So I'll put

00:07:07.200 --> 00:07:11.500
a link in the show notes for that as well. Nice. Yep. So I was talking about waiting. You're talking

00:07:11.500 --> 00:07:14.480
about what, being faster? That sounds better than just waiting around.

00:07:14.660 --> 00:07:21.340
Yes. Being faster. Well, maybe being faster. Not sure. So I'm still on the fence. Anyway,

00:07:21.340 --> 00:07:25.880
so virtual environments. I use virtual environments. Do you use virtual environments?

00:07:25.880 --> 00:07:30.880
Anytime I have to install any, if pip install has to be typed, there's a virtual environment

00:07:30.880 --> 00:07:31.360
involved. Yeah.

00:07:31.360 --> 00:07:36.200
Yeah. I use it for everything. Even if I've got a machine, like a build machine that really only has

00:07:36.200 --> 00:07:41.040
one Python environment and I'm only using it for one thing, I still set up a virtual environment.

00:07:41.040 --> 00:07:50.100
It's just always. And I've been, since the Python 3 started, Python 3 packaged VENV with Python. So

00:07:50.100 --> 00:07:57.540
you can create virtual environments just with the built-in VENV package. And I've been using that.

00:07:57.540 --> 00:08:05.220
Now, before that was in there, and if you were in Python 2 land, you needed to use the pip installable

00:08:05.220 --> 00:08:14.400
virtualenv package. Now, it is still updated and it is still being maintained. And I noticed,

00:08:14.400 --> 00:08:20.520
this was a conversation that started on Twitter this morning, that the virtual environment was still

00:08:20.520 --> 00:08:26.380
around and it was, maybe you should use that. So I went and checked it out again, the documentation

00:08:26.380 --> 00:08:32.980
for it. And it says, virtualenv is a tool to create isolated Python environments. We know this.

00:08:32.980 --> 00:08:38.900
Since Python 3.3, I guess, a subset of, has been integrated into the standard library. Yep.

00:08:38.900 --> 00:08:47.240
The VENV module does not offer all the features of this library. Just to name some of the prominent ones,

00:08:47.940 --> 00:08:55.820
VENV is slower. And it's not extendable. And it cannot create environments with multiple Python

00:08:55.820 --> 00:09:03.220
versions. And you can't update it with pip. And it doesn't have a programmatic API. Now, most of that,

00:09:03.220 --> 00:09:10.500
I just really don't care about. But the slower part, I do care about. So I gave it a shot this morning. I

00:09:10.500 --> 00:09:16.760
used time, the time function on the command line, just to time a couple of commands, created virtual

00:09:16.760 --> 00:09:23.100
environments with both VENV and virtualenv. And yeah, VENV takes a little over two seconds,

00:09:23.100 --> 00:09:29.900
two and a half seconds to finish, whereas the virtualenv version takes like quite a bit under

00:09:29.900 --> 00:09:36.560
half a second. So that's a lot. And I mean, if I'm doing a lot of virtual environments, I might care.

00:09:36.560 --> 00:09:43.100
Now, one of the things I was like, coming back and forth, why would I use VENV then if virtualenv

00:09:43.100 --> 00:09:50.780
is faster? Well, you have to pip install virtualenv. And so I'll have to remember to do that.

00:09:50.780 --> 00:09:56.320
I don't think I'll start teaching people this because it's just one more complication thing.

00:09:56.320 --> 00:10:01.720
And a couple of seconds isn't that big of a deal. And I still like the prompt, the --prompt.

00:10:01.720 --> 00:10:06.720
Virtualenv supports that too, but it handles it different. It doesn't wrap your prompt in

00:10:06.720 --> 00:10:12.680
parentheses. And maybe that's just a nicety, but I kind of like it. I'm not sure. I'm on the fence as

00:10:12.680 --> 00:10:15.340
to whether I should switch or use it.

00:10:15.340 --> 00:10:20.900
To me, it feels like I'm going to stick with VENV. For a long time, I saw virtualenv as just like,

00:10:20.900 --> 00:10:26.220
it's legacy stuff. It's there because before Python 3.3, you didn't have VENV built in,

00:10:26.220 --> 00:10:29.120
so you're going to need it. And a lot of the tutorials talked about it and whatnot.

00:10:29.660 --> 00:10:34.140
We recently covered it about why it got a big update and a lot of the things that it does that

00:10:34.140 --> 00:10:39.580
are nice. And the speed is cool. Maybe it wouldn't be that hard to adopt the --prompt

00:10:39.580 --> 00:10:45.580
dot feature, right? It's open source, right? It could get a PR that does that. That would be pretty

00:10:45.580 --> 00:10:51.320
cool, actually. It probably should just so it's consistent. But to me, the idea of having another

00:10:51.320 --> 00:10:57.560
thing I've got to install somewhere, probably into my user profiles, Python packages, so that then I can

00:10:57.560 --> 00:11:01.720
then create virtual environments so that I can then install things over into that area, it's just

00:11:01.720 --> 00:11:08.840
it's fine for me. But as somebody who does courses and teaching and other stuff like presentations,

00:11:08.840 --> 00:11:13.880
like it just seems like, okay, you just lost how many people out of that, right? Like,

00:11:13.880 --> 00:11:19.000
you know, what is the value? Like you say, it's two seconds. One of the things what I would like to see,

00:11:19.000 --> 00:11:25.580
and it would be really nice, maybe that would even push me over the edge is it drives me crazy that I

00:11:25.580 --> 00:11:30.780
create a new virtual environment from the latest version of Python that I can possibly get on the planet.

00:11:30.780 --> 00:11:35.540
And it tells me that pip is out of date. Now virtual environment didn't do that for me this morning.

00:11:35.540 --> 00:11:40.580
So it created a virtual environment with the newest pip in it.

00:11:40.580 --> 00:11:45.420
Oh, okay. See, now that's pretty nice, because it's annoying to say, okay, what you do is create this virtual

00:11:45.420 --> 00:11:49.900
environment, and you pip install this thing. Oh, look, there's always going to be a warning. So every single

00:11:49.900 --> 00:11:55.020
time, what you're going to do is you're going to fix that warning by doing this, right? So if it grabs the latest,

00:11:55.020 --> 00:12:03.020
that's actually kind of cool. Now that I think of it, I have a alias in my shell, my startup that I just type

00:12:03.020 --> 00:12:10.380
V and V and V and it does the Python dash M V and V. And then it does an upgrade of pip.

00:12:11.420 --> 00:12:16.740
And first it activates it. And then it does an upgrade of pip and setup tools all in like,

00:12:16.740 --> 00:12:20.240
four characters. So that's what I've been doing these days.

00:12:20.240 --> 00:12:25.780
Yeah, I've got like a little snippet in my profile also that I'm using. Funny enough,

00:12:25.780 --> 00:12:31.240
I shared it recently on Twitter, just my like two line snippet that I used. And then people kept on

00:12:31.240 --> 00:12:35.940
telling me to use all these other tools. Oh, you could just use this. It's not just use this.

00:12:36.040 --> 00:12:40.640
It's just a two line snippet in a profile. It's not a big deal. I don't even have to know what it

00:12:40.640 --> 00:12:44.760
is. I just typed these three characters. I'm good. Why are you bothering me? Right? Why is it such a

00:12:44.760 --> 00:12:49.780
big deal? I know it's crazy. Yeah. Yeah. What I would really like to see in none of these address

00:12:49.780 --> 00:12:58.860
is that something like kind of like node JS, where it just has the virtual environment at the top level

00:12:58.860 --> 00:13:03.660
and it just walks up until it finds a virtual environment and maybe complains if it doesn't,

00:13:03.660 --> 00:13:08.760
or it does something like that. Right? Like something to the effect where you say here,

00:13:08.760 --> 00:13:12.620
I know that this is a feature. I forgot what it's called. It's like the local Python or something like

00:13:12.620 --> 00:13:17.360
that, but it's not just built into Python. So if I just went into that directory and tried to run it,

00:13:17.360 --> 00:13:22.820
it's not going to find and use that version of Python, you know? Oh, well, the durinv, there's a few,

00:13:22.820 --> 00:13:27.260
there are a few packages that do that. And that's one of the things that people are directing me to is

00:13:27.260 --> 00:13:32.060
durinv. Durinv is cool. We should talk about it as a separate item. It's worth it. But yeah,

00:13:32.060 --> 00:13:35.700
D-I-R-E-N-V is cool. Yeah. Yeah. We should talk about that sometime.

00:13:35.700 --> 00:13:38.800
Yeah. Yeah. Yeah. Don't use up all our items all in one show, man. Come on.

00:13:38.800 --> 00:13:45.840
Okay. Let's thank our sponsor. So DigitalOcean is sponsoring this episode and DigitalOcean just

00:13:45.840 --> 00:13:51.320
launched their virtual private cloud and new trust platform. Together, these make it easier

00:13:51.320 --> 00:13:55.960
to architect and run serious business applications with even stronger security and confidence.

00:13:56.060 --> 00:14:02.140
The virtual private cloud or VPC allows you to create multiple private networks for your account

00:14:02.140 --> 00:14:07.460
or your team instead of having just one private network. DigitalOcean can auto-generate your private

00:14:07.460 --> 00:14:14.000
network's IP address range, or you can specify your own. You can now configure droplets to behave as

00:14:14.000 --> 00:14:19.380
internet gateways. And Trust Platform is a new microsite provides you one place to get all your

00:14:19.380 --> 00:14:24.560
security and privacy questions answered and download their available security certifications.

00:14:24.720 --> 00:14:31.380
DigitalOcean is your trusted partner in the cloud. Visit pythonbytes.fm/DigitalOcean

00:14:31.380 --> 00:14:36.380
to get a $100 credit for new users to build something awesome.

00:14:36.380 --> 00:14:40.440
Yeah. Awesome. Thanks for supporting the show, DigitalOcean. So before you had to wait on me,

00:14:40.440 --> 00:14:42.060
didn't you, Brian? It was frustrating.

00:14:42.060 --> 00:14:43.740
I did have to wait.

00:14:43.740 --> 00:14:47.840
All right. Let me tell you about when you might not even be awaiting stuff and things are still slow.

00:14:47.840 --> 00:14:54.580
So there's this cool analysis done by Chris Wellens in an article called Latency in Asynchronous

00:14:54.580 --> 00:15:10.740
Python. I don't know if it talks about problems with asyncio directly, but it's more talks about when you have a misconception of how something works over there, and then you apply a couple of patterns or behaviors to it, it might not do what you think.

00:15:10.740 --> 00:15:22.260
So for probably the best example would be, he gives a couple. I'll focus on this one where it's basically generating too much work and what can be done about it.

00:15:22.520 --> 00:15:36.160
So he says, I was debugging a program that was having some significant problems, but it was based on asyncio, and it would eventually take really long time for network responses to come back.

00:15:36.160 --> 00:15:42.700
And it's made of basically two parts. One is this thing that has to send a little heartbeat or receive a heartbeat or something.

00:15:42.700 --> 00:15:49.680
I don't remember if it's inbound or outbound, but it has to go beep, beep, beep once every, let's say, millisecond, right?

00:15:49.680 --> 00:15:58.720
So there's an async function, and it just rips through, and just every one millisecond, it kicks off one of these heartbeats. Totally simple, right?

00:15:58.720 --> 00:16:06.780
You just say await asyncio dot whatever, like sleep for one millisecond, then do the thing, and then go on, right?

00:16:06.780 --> 00:16:14.040
You can basically allow other concurrent work to happen while you're awaiting this sort of timeout, and to do it on a regular time frame.

00:16:14.040 --> 00:16:20.100
And then there's this other stuff that has to do some computational work that takes not very long, like 10 milliseconds.

00:16:20.100 --> 00:16:25.960
So you're receiving a JSON request, you have to parse that JSON and do it like a little bit of work, right?

00:16:25.960 --> 00:16:38.280
So because asyncio runs really on a single thread, that 10 milliseconds is going to block out and stop the heartbeat for 10 milliseconds, which is, you know, whatever, it's fine.

00:16:38.280 --> 00:16:41.740
It's like there's some variability, but it's no big deal.

00:16:41.740 --> 00:16:52.440
However, if you run a whole bunch of these, in his example, Chris said, let's run 200 of these computational things and like just start them up so that they can get put into this queue of work to be done.

00:16:52.680 --> 00:16:54.700
Well, the way it works is it all gets scheduled.

00:16:54.700 --> 00:17:00.940
It says, okay, we have a heartbeat and we have these 200 little slices of work, each of which is going to take 10 milliseconds.

00:17:00.940 --> 00:17:06.680
And there's a bunch of stuff around them that makes them a little bit slower, the scheduling and whatnot.

00:17:06.680 --> 00:17:08.540
And then we have a bunch of more heartbeats.

00:17:08.540 --> 00:17:11.920
So it goes beep, beep, block for two seconds.

00:17:11.920 --> 00:17:18.540
Beep, beep, beep, where you would expect, okay, I've got all these heartbeats going and I've got 200 little async things.

00:17:18.540 --> 00:17:20.320
Let's like mix them up, right?

00:17:20.320 --> 00:17:24.300
Like kind of share it fairly and it does not do that at all.

00:17:24.300 --> 00:17:24.740
Oh, wow.

00:17:24.740 --> 00:17:29.920
So it talks about basically what some of the challenges are there.

00:17:29.920 --> 00:17:36.000
One is probably you shouldn't just give it that much work in some giant batch.

00:17:36.000 --> 00:17:45.500
You should, you know, give it less work at a time, like some kind of like work queue or, you know, he said, let's see if a semaphore can work.

00:17:45.500 --> 00:17:48.840
Now, I don't remember if semaphores are reentrant or not.

00:17:48.840 --> 00:17:49.720
It didn't work.

00:17:49.720 --> 00:17:52.300
The semaphore didn't help at all, actually.

00:17:52.300 --> 00:17:54.300
He said, we can use a semaphore to limit it to 10.

00:17:54.300 --> 00:17:56.780
But if semaphores are reentrant, this is all one thread.

00:17:56.780 --> 00:17:57.400
It doesn't matter.

00:17:57.400 --> 00:17:59.300
Like the semaphore won't block itself.

00:17:59.300 --> 00:18:04.100
So that's like this normal threading, locking and stuff like that.

00:18:04.140 --> 00:18:06.820
They kind of don't apply because there's not actual threading going on.

00:18:06.820 --> 00:18:08.640
So that doesn't really help.

00:18:08.640 --> 00:18:16.040
But he comes up with this example of that the asyncio has a job queue, which allows you to push work into it.

00:18:16.040 --> 00:18:18.240
And then you can like wait for it to be completed.

00:18:18.240 --> 00:18:22.560
And there's all sorts of cool patterns and like producer consumer stuff that you can put on there.

00:18:22.780 --> 00:18:24.680
So I actually put together an example.

00:18:24.680 --> 00:18:26.180
He has like little code snippets.

00:18:26.180 --> 00:18:30.580
I put together a running example in one whole program that demonstrates this.

00:18:30.580 --> 00:18:33.160
And I have a link to the gist in the show notes.

00:18:33.400 --> 00:18:38.740
And I also would like to just point out how much a fan of unsync I am, which I always get it.

00:18:38.740 --> 00:18:41.200
I always talk about when I can around this async stuff.

00:18:41.200 --> 00:18:55.660
Like unsync is a library that is 120 lines of Python and it unifies multiprocessing, asyncio, threading, thread, like the, all these different APIs into like a perfect thing that fits with async await.

00:18:55.660 --> 00:18:57.540
It's really, really nice.

00:18:57.540 --> 00:19:06.680
But applying like the standard unsync adjustments to this code to say like what you do is just put a decorator at unsync on the function.

00:19:06.680 --> 00:19:07.640
That's it.

00:19:07.640 --> 00:19:11.880
You still use await and async and await and all that kind of stuff.

00:19:11.880 --> 00:19:12.980
The problem is gone.

00:19:12.980 --> 00:19:13.820
Totally fixes it.

00:19:13.820 --> 00:19:14.200
Oh, really?

00:19:14.200 --> 00:19:16.460
Like you don't have to go to like crazy queues and all that.

00:19:16.460 --> 00:19:17.820
Like the problem is gone.

00:19:17.820 --> 00:19:18.540
It's fixed.

00:19:18.540 --> 00:19:20.300
Well, it's alleviated.

00:19:20.300 --> 00:19:25.220
It may still be like if you push it far enough under certain like more complex criteria.

00:19:25.220 --> 00:19:29.500
But the example that showed the problem, you just make them unsync and you await them.

00:19:29.500 --> 00:19:32.380
It just runs like you would have originally expected.

00:19:32.380 --> 00:19:33.840
Like unsync is so beautiful.

00:19:33.840 --> 00:19:34.580
That's cool.

00:19:34.580 --> 00:19:37.240
It doesn't change the way asyncio works.

00:19:37.240 --> 00:19:46.280
It basically says, okay, the async work is going to run on a background thread and this other computational stuff will fit into the API, but will technically run on its own thread.

00:19:46.480 --> 00:19:56.260
So it's not like changing the internals, but you use the same code and then now this doesn't have this problem because the way it slices them together is better, I think.

00:19:56.260 --> 00:19:57.600
Anyway, it's pretty interesting.

00:19:57.600 --> 00:19:58.200
It's worth a look.

00:19:58.200 --> 00:20:01.720
Also, I have a copy of that on the gist and you can check that out and run it too.

00:20:01.720 --> 00:20:02.520
That's pretty cool.

00:20:02.520 --> 00:20:11.540
So unsync allows you to possibly not think too much about whether you should have these things just be async or whether there should be threads or something.

00:20:11.760 --> 00:20:12.320
Is it?

00:20:12.320 --> 00:20:13.060
Yeah, cool.

00:20:13.060 --> 00:20:13.940
Yeah, it's really neat.

00:20:13.940 --> 00:20:15.300
It just cleans everything up.

00:20:15.300 --> 00:20:17.340
But I sure hope they don't deprecate it though.

00:20:17.340 --> 00:20:19.420
Oh, that's a better transition.

00:20:19.420 --> 00:20:23.120
I was going to say, speaking of the cleaning things up, but...

00:20:23.120 --> 00:20:23.760
That works too.

00:20:23.760 --> 00:20:25.300
We'll just do both transitions.

00:20:25.300 --> 00:20:27.820
So how to deprecate a PyPI package.

00:20:27.820 --> 00:20:33.900
So you've put up a PyPI package and for some reason you don't want it to be up there.

00:20:33.900 --> 00:20:35.140
I don't want a puppy anymore.

00:20:35.140 --> 00:20:36.360
Why do I have to take care of this?

00:20:36.360 --> 00:20:36.780
Yeah.

00:20:36.780 --> 00:20:39.220
So there's lots of reasons why this might happen.

00:20:39.220 --> 00:20:42.640
One of them might just be you accidentally...

00:20:42.640 --> 00:20:50.840
You didn't use the test PyPI and use the live one and you put up Foo or some variant of Foo and you didn't mean to.

00:20:50.840 --> 00:20:57.220
Maybe it's some other package that somebody took over and it's handling it better and you want people to use something else.

00:20:57.220 --> 00:20:59.540
But anyway, there's lots of reasons why you might.

00:21:00.120 --> 00:21:06.180
A guy named Paul McCann wrote a blog post about how to deprecate a PyPI package.

00:21:06.180 --> 00:21:09.680
So he gives a few options and I think these are cool.

00:21:09.680 --> 00:21:17.200
One of the interesting things that he mentions is that PyPI doesn't really give you direction as to what it should look like.

00:21:17.200 --> 00:21:18.460
Which one you should use.

00:21:18.460 --> 00:21:20.720
So he's giving his opinion, which is great.

00:21:21.400 --> 00:21:23.380
You might use a deprecation warning.

00:21:23.380 --> 00:21:26.220
And this doesn't really apply to entire packages.

00:21:26.220 --> 00:21:28.500
But let's say you've changed your API.

00:21:28.500 --> 00:21:30.520
So it might as well be listed here.

00:21:30.520 --> 00:21:37.200
It's a good thing to, instead of just ripping out parts of your API, leave them in there, but make deprecation warnings in there.

00:21:37.200 --> 00:21:39.620
They really should be errors instead of warnings.

00:21:39.620 --> 00:21:46.040
If you're really taking them out and just having the warning, something like an assert is probably better.

00:21:46.640 --> 00:21:50.100
But there is a good thing to think about whether, don't just rip it out, maybe.

00:21:50.100 --> 00:21:50.580
I don't know.

00:21:50.580 --> 00:21:54.340
But if you rip it out completely, a assert will happen automatically.

00:21:54.340 --> 00:21:55.480
So maybe that's a good thing.

00:21:55.480 --> 00:21:58.560
As far as packages, though, you could just delete it.

00:21:58.560 --> 00:22:01.700
So you can, PyPI does allow you to remove packages.

00:22:01.700 --> 00:22:06.060
I don't think that that's probably the right thing to do, usually, ever.

00:22:06.060 --> 00:22:11.120
Unless you just push something up and it was an accident, then deleting it is fine.

00:22:11.120 --> 00:22:13.820
But if it's been up there for a while and people are using it,

00:22:14.140 --> 00:22:17.540
deleting it has a problem that somebody else could take over the name.

00:22:17.540 --> 00:22:23.520
And possibly a malicious package could take over the name and start to have people having install it.

00:22:23.520 --> 00:22:25.120
So there's problems with that.

00:22:25.120 --> 00:22:27.460
So it's probably not a good choice most of the time.

00:22:27.460 --> 00:22:30.120
The last two options are more reasonable.

00:22:30.120 --> 00:22:31.700
There's a redirect shim.

00:22:31.700 --> 00:22:33.260
So this is an example.

00:22:33.260 --> 00:22:37.040
Like, let's say there's an obvious package that is compatible,

00:22:37.040 --> 00:22:41.920
that is being better maintained, and you want to push people over to there.

00:22:42.420 --> 00:22:46.260
If it's really very compatible, you can add a setup, a shim that just,

00:22:46.260 --> 00:22:50.180
and there's some code examples here to just, if somebody installs it,

00:22:50.180 --> 00:22:54.060
it just installs the other package also that people should be using.

00:22:54.060 --> 00:22:55.080
I know if you want it. We'll give you that one.

00:22:55.080 --> 00:22:55.340
Yeah.

00:22:55.340 --> 00:22:59.660
And even having, if somebody imports your package,

00:22:59.660 --> 00:23:01.740
it really just imports the other package too.

00:23:01.740 --> 00:23:06.100
That's a little weird, but it is interesting that it's an option.

00:23:06.440 --> 00:23:11.300
The thing I really like that probably the best is just a way to fail during install.

00:23:11.300 --> 00:23:15.560
And there's a code example here for if somebody at pip installs something,

00:23:15.560 --> 00:23:22.360
and all the packaging works, but the install part will throw an error,

00:23:23.040 --> 00:23:27.000
and you can put a message there redirecting people to use a different package,

00:23:27.000 --> 00:23:29.840
or maybe just explain why you ripped this one out.

00:23:29.840 --> 00:23:31.900
So I think I like the last one best.

00:23:31.900 --> 00:23:33.920
So most of those are my commentary.

00:23:33.920 --> 00:23:36.720
But there's some options for how to deal with it.

00:23:36.720 --> 00:23:37.820
So I thought that was good.

00:23:37.820 --> 00:23:41.500
Yeah, I really like the sort of, I tried to pip install it,

00:23:41.500 --> 00:23:44.060
and it gives you, instead of just failing or being gone,

00:23:44.060 --> 00:23:46.100
it actually gives you a meaningful message.

00:23:46.100 --> 00:23:49.220
Like, you should use this other package, we're done.

00:23:49.220 --> 00:23:51.720
If you really intend to delete it, that's probably it.

00:23:51.720 --> 00:23:53.320
Yeah, and one of the interesting things,

00:23:53.320 --> 00:23:57.120
the last couple, the redirect shim and the fail during install,

00:23:57.120 --> 00:24:00.380
he gives example packages that do this.

00:24:00.380 --> 00:24:03.000
And some of these are just mistyped things.

00:24:03.000 --> 00:24:05.400
Like, if people mistype something,

00:24:05.400 --> 00:24:10.260
maybe they meant something else, and redirecting there.

00:24:10.260 --> 00:24:10.580
Yeah.

00:24:10.580 --> 00:24:13.620
It seems so write-only over at PyPI.

00:24:14.340 --> 00:24:17.820
And, you know, if you make a mistake, it's not good.

00:24:17.820 --> 00:24:19.380
So knowing what to do.

00:24:19.380 --> 00:24:20.700
I mean, people depend on it, right?

00:24:20.700 --> 00:24:22.280
If you yank it out, then it's trouble.

00:24:22.280 --> 00:24:24.440
Yeah, but if it's mistake-driven, though,

00:24:24.440 --> 00:24:26.640
make sure you use the test interface first

00:24:26.640 --> 00:24:29.920
to play with things before you push garbage up there.

00:24:29.920 --> 00:24:33.060
Also, I'd really like people to not squat on names.

00:24:33.060 --> 00:24:35.620
There's a lot of cool package names out there

00:24:35.620 --> 00:24:38.100
that really have nothing meaningful there

00:24:38.100 --> 00:24:40.600
because somebody decided they wanted to grab a name

00:24:40.600 --> 00:24:42.520
and then didn't do anything with it.

00:24:42.520 --> 00:24:43.040
That's lame.

00:24:43.040 --> 00:24:43.580
Don't do that.

00:24:43.720 --> 00:24:44.640
Yeah, that's definitely lame.

00:24:44.640 --> 00:24:46.600
On the other hand, there are some times I'm like,

00:24:46.600 --> 00:24:48.160
how did you just get that name?

00:24:48.160 --> 00:24:51.480
There'll be like a new package, like secure or something like that.

00:24:51.480 --> 00:24:54.080
Like, how did you get that after all this time, right?

00:24:54.080 --> 00:24:54.900
It's crazy.

00:24:54.900 --> 00:24:56.020
Yeah, definitely.

00:24:56.020 --> 00:24:56.760
Yeah, yeah.

00:24:56.760 --> 00:25:00.260
Or up to 236,000 packages.

00:25:00.260 --> 00:25:01.020
That's pretty insane.

00:25:01.020 --> 00:25:01.360
Yeah.

00:25:01.500 --> 00:25:04.680
So, Brian, would you like me to enlighten you a little bit in the listeners?

00:25:04.680 --> 00:25:06.780
Yes, please enlighten me.

00:25:06.780 --> 00:25:11.160
So, last time you brought up a cool progress bar.

00:25:11.160 --> 00:25:12.760
It was either the last time or the time before.

00:25:12.760 --> 00:25:15.600
And it did all sorts of cool stuff.

00:25:15.600 --> 00:25:17.420
But here's yet another one.

00:25:17.480 --> 00:25:19.280
Again, an example of our listeners saying,

00:25:19.280 --> 00:25:20.960
oh, here's three cool things you talked about,

00:25:20.960 --> 00:25:23.220
but did you know there are these four others you've never heard of?

00:25:23.220 --> 00:25:23.600
Yeah.

00:25:23.720 --> 00:25:30.240
So, Avram Lubkin sent over his progress bar package called Enlighten.

00:25:30.240 --> 00:25:32.060
And it's actually pretty cool.

00:25:32.060 --> 00:25:35.460
Like, there's a bunch of cool progress bars with nice animations and stuff.

00:25:35.460 --> 00:25:39.620
But there's a few features of Enlighten that might make you choose it.

00:25:39.620 --> 00:25:44.320
One is you can have colored progress bars, which is nice.

00:25:44.320 --> 00:25:47.200
But more importantly, you can have multicolored progress bars.

00:25:47.200 --> 00:25:50.760
So, let me throw out an example that I think would connect for you,

00:25:50.760 --> 00:25:52.620
given that you're a fan of pytest.

00:25:52.620 --> 00:25:56.500
Like, if you run some sort of series or sequence of operations,

00:25:56.500 --> 00:25:58.320
and you want to show how far you're making it,

00:25:58.320 --> 00:26:01.480
but they have multiple outcomes, like red is failure, green is success,

00:26:01.480 --> 00:26:03.940
and yellow is, like, skip or something like that.

00:26:03.940 --> 00:26:07.240
You could have a progress bar that has three segments,

00:26:07.240 --> 00:26:11.320
a red segment, a yellow segment, and a green segment.

00:26:11.320 --> 00:26:15.120
And it could be all one bar, but it could kind of, like, show you as it grows.

00:26:15.120 --> 00:26:16.740
Here's the level of failure.

00:26:16.740 --> 00:26:18.940
Here's the level of success, and so on.

00:26:18.940 --> 00:26:23.100
All with color, 24-bit color, not just, like, eight colors either.

00:26:23.100 --> 00:26:23.640
Oh, yeah.

00:26:23.640 --> 00:26:24.420
That'd be great.

00:26:24.420 --> 00:26:25.120
That's good.

00:26:25.120 --> 00:26:26.700
And then I said, those just go off.

00:26:26.700 --> 00:26:31.760
The other one is a lot of these progress bars, they'll sort of control,

00:26:31.760 --> 00:26:33.540
they'll be rewriting the screen, right?

00:26:33.540 --> 00:26:35.860
They'll be putting stuff across as it's happening.

00:26:35.940 --> 00:26:37.840
But if you happen to do, like, a print statement,

00:26:37.840 --> 00:26:42.120
effectively writing a standard out or an exception that writes a standard error

00:26:42.120 --> 00:26:46.020
or something like that, it, you know, messes them all up, right?

00:26:46.020 --> 00:26:52.120
So this one works well, even allows you to write print statements while it's working.

00:26:52.120 --> 00:26:54.700
So the print statements kind of flow by above it,

00:26:54.700 --> 00:26:57.480
but it's, you know, whatever part of the screen it's taken over,

00:26:57.480 --> 00:26:59.660
it still is managing that as well.

00:26:59.660 --> 00:27:03.880
So it overrides what print means or stand it out and sends it where it belongs.

00:27:04.120 --> 00:27:04.480
That's cool.

00:27:04.480 --> 00:27:05.720
Yeah, that's pretty cool.

00:27:05.720 --> 00:27:08.960
It also automatically handles resizing, except on Windows.

00:27:08.960 --> 00:27:11.060
And where it says except on Windows,

00:27:11.060 --> 00:27:13.880
I'm not sure if that means on the new terminal,

00:27:13.880 --> 00:27:16.460
Windows terminal that they came out with that's much closer

00:27:16.460 --> 00:27:18.740
to what we have over on Mac and Linux,

00:27:18.740 --> 00:27:21.620
or if it just means it doesn't work on Windows at all.

00:27:21.620 --> 00:27:25.780
I suspect it might work on the new Windows terminal that just went 1.0,

00:27:25.780 --> 00:27:27.680
but certainly not on CMD.

00:27:27.680 --> 00:27:28.040
Okay.

00:27:28.040 --> 00:27:29.720
Who uses CMD?

00:27:29.720 --> 00:27:30.180
Okay.

00:27:30.180 --> 00:27:32.620
I mean, that's what comes with Windows.

00:27:32.740 --> 00:27:35.080
If you don't like go out of the way to get something, right?

00:27:35.080 --> 00:27:37.520
Like commander or the new terminal or something like that.

00:27:37.520 --> 00:27:40.400
Yeah, but you gotta install Git at least on Windows anyway,

00:27:40.400 --> 00:27:41.600
and then you got Bash.

00:27:41.600 --> 00:27:42.240
Yeah, that's true.

00:27:42.240 --> 00:27:42.640
It comes with it.

00:27:42.640 --> 00:27:43.300
That's true.

00:27:44.300 --> 00:27:50.360
So like all good things that have actions and behaviors and are visual,

00:27:50.360 --> 00:27:54.000
there's a nice little animation even on the PyPI.org page.

00:27:54.000 --> 00:27:57.640
So if you go there, you can watch and actually see the stuff scrolling by.

00:27:57.640 --> 00:28:00.340
It's like an animated GIF right on the PyPI page.

00:28:00.340 --> 00:28:01.260
So very, very nice.

00:28:01.260 --> 00:28:01.720
Well done.

00:28:01.720 --> 00:28:03.780
You know, the multicolored progress bars,

00:28:03.780 --> 00:28:05.920
it does seem pretty awesome if that's your use case.

00:28:05.920 --> 00:28:06.960
I want rainbow ones.

00:28:07.020 --> 00:28:08.020
I want to do a rainbow.

00:28:08.020 --> 00:28:08.500
Yes.

00:28:08.500 --> 00:28:13.960
Maybe with like little unicorns just shooting out of it and just like all sorts of crazy.

00:28:13.960 --> 00:28:14.220
Yeah.

00:28:14.220 --> 00:28:14.840
Sounds good.

00:28:14.840 --> 00:28:15.260
Yes.

00:28:15.260 --> 00:28:16.800
Stars and unicorns.

00:28:16.800 --> 00:28:17.160
Yes.

00:28:17.160 --> 00:28:17.820
It would be perfect.

00:28:17.820 --> 00:28:18.120
Yeah.

00:28:18.120 --> 00:28:19.100
Let's have that.

00:28:19.100 --> 00:28:24.400
And people are starting to catch on that we like animations because they'll include it in the suggestion.

00:28:24.400 --> 00:28:26.180
And by the way, it has an animation.

00:28:26.180 --> 00:28:26.820
Good job.

00:28:26.820 --> 00:28:27.180
You can watch it here.

00:28:27.180 --> 00:28:27.420
Yeah.

00:28:27.420 --> 00:28:27.900
Good job.

00:28:27.900 --> 00:28:28.480
Bringing it up.

00:28:28.480 --> 00:28:28.900
Yeah.

00:28:29.840 --> 00:28:30.580
You're part of it.

00:28:30.580 --> 00:28:30.860
Awesome.

00:28:30.860 --> 00:28:33.100
Well, nice work on that progress bar library.

00:28:33.100 --> 00:28:34.840
It seems simple and well done.

00:28:34.840 --> 00:28:37.260
Speaking of unicorns, I want to talk about oceans.

00:28:37.260 --> 00:28:40.040
Wait, unicorns don't live in the ocean.

00:28:40.040 --> 00:28:40.460
Mermaids.

00:28:40.460 --> 00:28:41.720
Mermaids.

00:28:41.720 --> 00:28:42.740
Let's go with mermaids.

00:28:42.740 --> 00:28:44.500
So I want to talk about Code Ocean.

00:28:44.500 --> 00:28:47.340
So this was contributed by Daniel Mulkey.

00:28:47.340 --> 00:28:50.240
So this is a pretty neat thing.

00:28:50.240 --> 00:28:54.540
So Code Ocean is a paid service, but there's a free tier.

00:28:55.080 --> 00:29:01.780
And it's a research collaboration platform that supports researchers from the beginning of a project through publication.

00:29:01.780 --> 00:29:04.460
So this is kind of this neat thing.

00:29:04.460 --> 00:29:07.280
I'm going to read a little bit from their about page.

00:29:07.280 --> 00:29:17.940
We built a platform that can help give researchers back 20% of the time they spend troubleshooting technology in order to run and reproduce past work before completing new experiments.

00:29:18.680 --> 00:29:30.640
And Code Ocean is an open access platform for code and data where users can develop, share, publish, and download code through a web browser, eliminating the need to install software, blah, blah, blah, blah.

00:29:30.640 --> 00:29:33.220
Anyway, mission is to make research easier.

00:29:33.220 --> 00:29:54.980
So this idea is you can have code snippets like Jupyter and Python and even things like MATLAB and C++ code running with the data in this kind of environment that you can collaborate with other people and just sort of build up these data sets and the science and the code and all bundled together.

00:29:55.300 --> 00:29:56.880
And it's pretty cool.

00:29:56.880 --> 00:29:59.180
It also collaborates with some.

00:29:59.180 --> 00:30:08.880
One of the goals of it is to be able to have all of this reproducible code and data together in a form that's acceptable to journals.

00:30:09.840 --> 00:30:23.380
And one of the reasons why it was contributed is Daniel said that one of the peer-reviewed journals that he reads, it happened to be SPIE's optical engineering journal, recommended this platform for associating code with the article.

00:30:23.380 --> 00:30:31.180
So people trying to do science and be published, associating a Code Ocean space with it is an option.

00:30:31.180 --> 00:30:31.740
That's cool.

00:30:31.860 --> 00:30:37.380
And if it gets accepted by editors as, yeah, that's what you do, then it just makes it easier.

00:30:37.380 --> 00:30:39.880
It's kind of like saying, oh, you have an open source project.

00:30:39.880 --> 00:30:41.100
What's the GitHub URL?

00:30:41.100 --> 00:30:41.800
Right?

00:30:41.800 --> 00:30:42.680
Not is it on GitHub?

00:30:42.680 --> 00:30:44.060
Just where on GitHub is it?

00:30:44.060 --> 00:30:44.320
Yeah.

00:30:44.320 --> 00:30:50.260
I do technically know that there's GitLab and other places, but most of the code lives on GitHub is what I was getting at, right?

00:30:50.260 --> 00:30:52.020
Yeah.

00:30:52.020 --> 00:30:52.340
Cool.

00:30:52.340 --> 00:30:53.600
Well, this looks pretty neat.

00:30:53.600 --> 00:31:00.240
I do think there's a lot of interesting takes on reproducibility in science, and that's definitely a good thing.

00:31:00.680 --> 00:31:10.140
There's this, there's Binder, which is doing a lot of interesting stuff, although not as focused on exact reproducibility, but still nice.

00:31:10.140 --> 00:31:14.620
There's Gigantum, which is also a cool platform for this kind of stuff.

00:31:14.620 --> 00:31:17.580
So there's a lot of options, and it's nice to see more of them like Code Ocean.

00:31:17.580 --> 00:31:17.860
Yeah.

00:31:17.860 --> 00:31:18.480
Nice.

00:31:18.480 --> 00:31:20.220
Well, that's R6.

00:31:20.220 --> 00:31:22.380
Do you have anything extra for us today?

00:31:22.380 --> 00:31:27.460
I'm going to try to connect this to Python because I am excited about it.

00:31:27.460 --> 00:31:34.600
How long has it been since astronauts have been launched into space from NASA and from the U.S.?

00:31:34.600 --> 00:31:38.800
It's been a really long time, ever since the space shuttle got shut down four or five years ago.

00:31:38.800 --> 00:31:42.000
I heard it was like over 10, but I could have heard wrong.

00:31:42.000 --> 00:31:42.840
It's very possible.

00:31:42.840 --> 00:31:44.080
It's been a very long time.

00:31:44.260 --> 00:31:51.540
So today, I know this doesn't help you folks listening because the time it takes us to get this episode out, but hopefully this went well.

00:31:51.540 --> 00:31:58.800
But I'm super excited for SpaceX's launch in collaboration with NASA to send two astronauts up into space.

00:31:58.800 --> 00:31:59.720
Wow.

00:31:59.720 --> 00:32:02.100
Are those guys brave to get on to one of these rockets?

00:32:04.600 --> 00:32:09.540
But also, I think there's probably somewhere in the mix a lot of Python in action.

00:32:09.540 --> 00:32:18.960
If you go to SpaceX, they had, last time I looked at one random point a couple months ago, they had 92 open positions for Python developers.

00:32:18.960 --> 00:32:19.520
Oh, wow.

00:32:19.520 --> 00:32:24.920
I don't know if that's 92 people they're looking for, but at least there's 92 roles they were trying to fill.

00:32:25.200 --> 00:32:27.340
There could be multiple people into any one role.

00:32:27.340 --> 00:32:28.800
So that's a lot of Python.

00:32:28.800 --> 00:32:33.960
And so somehow in this launch, there's got to be some interesting stories around Python.

00:32:33.960 --> 00:32:38.880
And this is mostly to say, one, it's awesome that SpaceX and NASA are doing this.

00:32:38.880 --> 00:32:40.400
Hopefully this goes well.

00:32:40.400 --> 00:32:41.580
Lots and lots of luck to that.

00:32:41.580 --> 00:32:51.660
But also, if anyone knows how to connect us with the people inside SpaceX doing awesome rocket stuff with Python, those would make great stories.

00:32:51.660 --> 00:32:54.740
We would love to hear about those and introductions and whatnot.

00:32:54.880 --> 00:32:55.500
Yeah, that would be cool.

00:32:55.500 --> 00:32:56.900
I'd love to hear more about that.

00:32:56.900 --> 00:32:57.480
Yeah.

00:32:57.480 --> 00:32:58.760
I do hope it goes well.

00:32:58.760 --> 00:33:04.760
And I heard that possibly there was weather problems that might crop up, but we'll...

00:33:04.760 --> 00:33:06.460
Well, maybe people will get to watch this.

00:33:06.460 --> 00:33:07.700
Knock on wood.

00:33:07.700 --> 00:33:09.160
Maybe it'll be delayed for a week.

00:33:09.160 --> 00:33:09.500
We'll see.

00:33:09.500 --> 00:33:11.060
Awesome.

00:33:11.060 --> 00:33:11.540
How about you?

00:33:11.540 --> 00:33:11.900
What do you got?

00:33:11.900 --> 00:33:14.960
I just downloaded 3.90 beta 1.

00:33:14.960 --> 00:33:18.460
So Python 3.9 beta 1 is available for testing.

00:33:18.460 --> 00:33:24.760
If you are maintaining a package or any other maintaining your application, you probably ought to download

00:33:24.760 --> 00:33:27.000
and make sure your stuff works with 3.9.

00:33:27.000 --> 00:33:27.840
Oh, yeah.

00:33:27.840 --> 00:33:28.200
That's cool.

00:33:28.200 --> 00:33:33.980
And because it's a beta now, it should be frozen in terms of features and APIs and stuff, right?

00:33:33.980 --> 00:33:35.880
It's no longer changing.

00:33:35.880 --> 00:33:40.340
So it's now time to start making sure your stuff works and yelling if it doesn't.

00:33:40.340 --> 00:33:40.620
Yeah.

00:33:40.620 --> 00:33:41.560
Right.

00:33:41.560 --> 00:33:49.220
And another reason to download it is the prompt, VNV with the prompt with the magic dot that turns your directory name.

00:33:49.220 --> 00:33:49.560
Yeah.

00:33:49.560 --> 00:33:50.340
Yeah.

00:33:50.340 --> 00:33:51.720
That is in 3.9.

00:33:51.720 --> 00:33:51.980
Yeah.

00:33:51.980 --> 00:33:52.420
Super cool.

00:33:52.420 --> 00:33:53.020
Awesome.

00:33:53.020 --> 00:33:55.980
Well, that's not very funny, but I could tell you something that is.

00:33:56.140 --> 00:33:58.460
And it's very relevant to your item here, actually.

00:33:58.460 --> 00:33:58.980
Okay.

00:33:58.980 --> 00:33:59.640
You ready for this?

00:33:59.640 --> 00:34:04.100
So open up this link here and I'll put the link in the show notes because this is a visual.

00:34:04.100 --> 00:34:05.320
I got to describe it to you.

00:34:05.320 --> 00:34:06.960
So this was sent over by Stephen Howell.

00:34:06.960 --> 00:34:08.120
Thank you for that.

00:34:08.120 --> 00:34:12.060
And this would be better during Halloween, but Halloween is far away.

00:34:12.120 --> 00:34:13.280
So we're going to do it this way.

00:34:13.280 --> 00:34:18.140
So there's a person standing around and there's a ghost standing behind them, right?

00:34:18.140 --> 00:34:18.440
Yeah.

00:34:18.440 --> 00:34:19.540
And the ghost says, boo.

00:34:19.540 --> 00:34:21.000
Person doesn't react.

00:34:21.000 --> 00:34:21.820
Boo.

00:34:21.820 --> 00:34:22.980
Person doesn't react.

00:34:22.980 --> 00:34:23.640
Boo.

00:34:23.640 --> 00:34:25.540
Person doesn't react.

00:34:25.540 --> 00:34:27.420
The person says, Python 2.7.

00:34:27.420 --> 00:34:28.240
Ah!

00:34:28.240 --> 00:34:29.860
The person runs away.

00:34:29.860 --> 00:34:33.020
Yeah, this is great.

00:34:33.020 --> 00:34:33.700
It's good, right?

00:34:33.700 --> 00:34:33.960
Yeah.

00:34:33.960 --> 00:34:34.480
Yeah.

00:34:34.480 --> 00:34:35.260
You got it as well.

00:34:35.260 --> 00:34:35.800
What do you got here?

00:34:35.800 --> 00:34:38.720
Well, I'm going to get haters for this, but I'm going to say it anyway.

00:34:38.720 --> 00:34:44.940
So somebody named Bert sent us a meta joke because we have used PyJokes before.

00:34:44.940 --> 00:34:45.980
We love PyJokes.

00:34:45.980 --> 00:34:47.620
And I'm going to modify it a little bit.

00:34:47.620 --> 00:34:50.160
So what does PyJokes have in common with Java?

00:34:50.160 --> 00:34:53.480
It gets updated all the time, but never gets any better.

00:34:53.480 --> 00:34:56.360
That's pretty funny.

00:34:56.360 --> 00:35:00.420
I don't even really use Java, but I have a Java tool on my desktop.

00:35:00.420 --> 00:35:02.940
And so I get like, Java's updated.

00:35:02.940 --> 00:35:03.940
Do you want to do the update?

00:35:03.940 --> 00:35:04.800
All the time.

00:35:04.800 --> 00:35:06.040
Make it stop.

00:35:06.040 --> 00:35:06.420
Yeah.

00:35:06.420 --> 00:35:07.140
Make it stop.

00:35:07.140 --> 00:35:07.940
Yeah, that's funny.

00:35:08.420 --> 00:35:09.260
Yeah, PyJokes is good.

00:35:09.260 --> 00:35:13.460
If you all need some programming jokes, just pip install --user PyJokes.

00:35:13.460 --> 00:35:15.900
And then you can type PyJoke anytime you want.

00:35:15.900 --> 00:35:16.200
Yeah.

00:35:16.200 --> 00:35:21.320
I had to change it because the original joke was like about Flash, Adobe Flash.

00:35:21.320 --> 00:35:22.820
And who has that?

00:35:22.820 --> 00:35:23.960
Is that even a thing anymore?

00:35:23.960 --> 00:35:24.840
Yeah.

00:35:24.840 --> 00:35:26.500
I don't even think it gets updated anymore.

00:35:26.500 --> 00:35:26.980
I don't know.

00:35:26.980 --> 00:35:27.480
Maybe it does.

00:35:27.480 --> 00:35:28.980
I sure hope it's not on my computer.

00:35:28.980 --> 00:35:29.880
Yeah.

00:35:29.880 --> 00:35:31.880
It's a security phone.

00:35:31.880 --> 00:35:33.180
Yeah, it totally is.

00:35:33.180 --> 00:35:33.740
Awesome.

00:35:33.740 --> 00:35:34.400
All right.

00:35:34.400 --> 00:35:34.920
Well, very funny.

00:35:34.920 --> 00:35:35.480
All right.

00:35:35.480 --> 00:35:35.960
Thank you.

00:35:35.960 --> 00:35:36.260
Yep.

00:35:36.260 --> 00:35:36.580
You bet.

00:35:36.580 --> 00:35:36.920
Bye-bye.

00:35:37.420 --> 00:35:38.940
Thank you for listening to Python Bytes.

00:35:38.940 --> 00:35:41.400
Follow the show on Twitter at Python Bytes.

00:35:41.400 --> 00:35:44.440
That's Python Bytes as in B-Y-T-E-S.

00:35:44.440 --> 00:35:47.320
And get the full show notes at Python Bytes.fm.

00:35:47.320 --> 00:35:52.400
If you have a news item you want featured, just visit Python Bytes.fm and send it our way.

00:35:52.400 --> 00:35:54.440
We're always on the lookout for sharing something cool.

00:35:54.440 --> 00:35:55.620
This is Brian Okken.

00:35:55.620 --> 00:35:59.700
And on behalf of myself and Michael Kennedy, thank you for listening and sharing this podcast

00:35:59.700 --> 00:36:00.920
with your friends and colleagues.