Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to
your earbuds. This is episode 184, recorded May 27th, 2020. I'm Brian Okken. And I'm Michael
Kennedy. And this episode is sponsored by DigitalOcean. Thank you, DigitalOcean. Yeah,
thanks, DigitalOcean. Really appreciate it. Well, I'm waiting, Michael. You're waiting?
What are you waiting for? I'm waiting for the next AsyncIO story.
I guess it's my turn, isn't it? Okay. Sorry, let me see if I can get this right.
So the topic I want to talk about is waiting in AsyncIO. Yeah, so the magic of AsyncIO,
which was introduced in Python 3.4, never really appeared until Python 3.5 when the async and await
keywords came into being, which let you write code that looks like standard single-threaded serial code,
but actually is multi-threaded, or at least parallel concurrent to some degree. Depends on
how you're running it, whether it's truly multi-threaded. Anyway, there's a lot of options,
let's say, on how you can interact with these co-routines and these tasks that are generated by the AsyncIO
framework. And anytime there's like four ways to do something in programming, you should be asking
yourself, one, why are there four ways to do this? But more importantly, when does way one apply best?
And what scenario should I use way three? And what is the trade-off between two and four?
And so on, right? So that's the case with AsyncIO. There's tons of ways to wait or await things.
And Hank, get the pronunciation right for me. I know you got it.
You got it. That's good.
Sorry.
That's just a running thing. I can never get it right.
No. Well, the problem is, I think I had it right. And then we went back and forth so many times with so
many variations. Now it's broken. Sorry about messing up your name there. So I wrote a great article,
though, called Waiting in AsyncIO that does exactly that, that says, here are all the ways,
here's the pluses, and here's the minuses, and the situations in which you apply them.
So if you're serious about using AsyncIO and you're building real things, basically this podcast
episode is for you because I have this and another one that bring two really cool ideas together.
But let's talk about the waiting one first.
So it's really easy to start doing some work, right? I can have two coroutines, let's say F and G,
and I could say I want the result of F by saying, you know, result equals await calling F,
and then result G equals await calling G. And that's fine if what you're looking for is more
concurrent execution of that part of code, right? So this is, say, in a web method, like a view,
someone makes a request, and there's not a lot you can do to make things go faster,
potentially for that one request, but you can say, let the server be less busy,
so it could handle like 10 or 20 times more of the same request, right? So this real simple,
like just await calling these functions, these Async functions, it will allow your system to scale
more, but it won't make things faster. Like for example, if you're trying to crawl 20 web pages,
this will not make it any faster, it'll just make your code more complicated. So don't do that.
So there's other ways which you want to do that. Another thing that I think a lot of people
don't quite get is when you call one of these Async functions, like Async Def Function Name,
when you call it, it doesn't actually start it until you either await it or create a task from it.
So if you call like F and then G, and you think you're going to come back and get to them later,
now they're running. No, actually, they're not unless you've created a task, which starts them.
So you either have to await them, which blocks, or create these tasks to like kick them off.
That's pretty interesting, right? Like that's not super obvious. I think normally when you call a
function, it does a thing. But here, not so much. So some other options, if you could call them both as
create them as tasks, and then you could await those tasks, right? Because their tasks are already
running, and then you await them both, whichever one first finishes first doesn't matter. You wait
till the first one's done, and maybe the second one's already done. So that's probably the pattern
that most people are going to be using. But you can also use Async IO Gather that takes one or more
awaitables as a star args, and then it waits for them all to finish, which is pretty cool. And that
itself is a future thing that you can await, right? So you would say await Async IO dot gather.
Oh, that's cool.
Yeah, gather is awesome, because I can create all these tasks. And I just say, I just need them all
to be done. And when that's done, we can get the results and carry on. And what's cool is when you
await gather, you get a tuple of results. So if I say Async IO gather function one or task one,
task two, then it returns the result one comma result two as a tuple. So you can gather them up and get
all the answers back, which is pretty cool. Yeah, that's really neat. Yeah, one of the problems
with gather, though, is you're saying I'm willing to wait forever for this to finish. And sometimes
that's fine. But sometimes things don't return correctly or ever or in the right amount of time.
So you can use wait for, which is nice and allows you to pass a timeout. But what's a little bit
better than a wait for is there's an Async timeout package on PyPI, which I'd not heard of. And you can
basically create a block that will a width block that will timeout. So you can say I want to have
an Async with timeout five seconds and then do a whole bunch of function calls and awaiting and all
that. And either they're all going to finish or when five seconds passes, everything gets canceled
that hasn't finished. That's cool. That's pretty cool, right? Another one that's really interesting
is I start a bunch of work. And then I would like to say I kick off, I'm doing web scraping,
and I want to go get the results of 20 web pages. I kick off 20 requests and then I want
to process them as they complete. Like the first one that's done, I want to work on that. Then
the next one, then the next one. So you can create a task or an iterable rather from saying AsyncIO.as
completed and you give it a bunch of tasks and it gives you an iterator that you can for in
over that gives you the first completed one, then the second completed one and just blocks until
the next one is completed. So you kick a bunch off and then you just say for completed tasks in AsyncIO.as
completed and you give it your running tasks. That's really slick, isn't it?
That is slick. Looks like it has a timeout also that you can add to it.
Yeah, very nice. Yep. You can give it a timeout indeed.
Now, there's a few more things covered in there and I didn't go over the trade-offs too much.
You know, here's the scenario where you use this and that. So if you really care about this,
two things to do. One is check out the article. It's got a lot of details and each subsection has
a little trade-offs. Here's the good, here's the bad, which is nice. And also you can check out my
Async course, which talks about this and a whole bunch of other things on Async as well. So I'll put
a link in the show notes for that as well. Nice. Yep. So I was talking about waiting. You're talking
about what, being faster? That sounds better than just waiting around.
Yes. Being faster. Well, maybe being faster. Not sure. So I'm still on the fence. Anyway,
so virtual environments. I use virtual environments. Do you use virtual environments?
Anytime I have to install any, if pip install has to be typed, there's a virtual environment
involved. Yeah.
Yeah. I use it for everything. Even if I've got a machine, like a build machine that really only has
one Python environment and I'm only using it for one thing, I still set up a virtual environment.
It's just always. And I've been, since the Python 3 started, Python 3 packaged VENV with Python. So
you can create virtual environments just with the built-in VENV package. And I've been using that.
Now, before that was in there, and if you were in Python 2 land, you needed to use the pip installable
virtualenv package. Now, it is still updated and it is still being maintained. And I noticed,
this was a conversation that started on Twitter this morning, that the virtual environment was still
around and it was, maybe you should use that. So I went and checked it out again, the documentation
for it. And it says, virtualenv is a tool to create isolated Python environments. We know this.
Since Python 3.3, I guess, a subset of, has been integrated into the standard library. Yep.
The VENV module does not offer all the features of this library. Just to name some of the prominent ones,
VENV is slower. And it's not extendable. And it cannot create environments with multiple Python
versions. And you can't update it with pip. And it doesn't have a programmatic API. Now, most of that,
I just really don't care about. But the slower part, I do care about. So I gave it a shot this morning. I
used time, the time function on the command line, just to time a couple of commands, created virtual
environments with both VENV and virtualenv. And yeah, VENV takes a little over two seconds,
two and a half seconds to finish, whereas the virtualenv version takes like quite a bit under
half a second. So that's a lot. And I mean, if I'm doing a lot of virtual environments, I might care.
Now, one of the things I was like, coming back and forth, why would I use VENV then if virtualenv
is faster? Well, you have to pip install virtualenv. And so I'll have to remember to do that.
I don't think I'll start teaching people this because it's just one more complication thing.
And a couple of seconds isn't that big of a deal. And I still like the prompt, the --prompt.
Virtualenv supports that too, but it handles it different. It doesn't wrap your prompt in
parentheses. And maybe that's just a nicety, but I kind of like it. I'm not sure. I'm on the fence as
to whether I should switch or use it.
To me, it feels like I'm going to stick with VENV. For a long time, I saw virtualenv as just like,
it's legacy stuff. It's there because before Python 3.3, you didn't have VENV built in,
so you're going to need it. And a lot of the tutorials talked about it and whatnot.
We recently covered it about why it got a big update and a lot of the things that it does that
are nice. And the speed is cool. Maybe it wouldn't be that hard to adopt the --prompt
dot feature, right? It's open source, right? It could get a PR that does that. That would be pretty
cool, actually. It probably should just so it's consistent. But to me, the idea of having another
thing I've got to install somewhere, probably into my user profiles, Python packages, so that then I can
then create virtual environments so that I can then install things over into that area, it's just
it's fine for me. But as somebody who does courses and teaching and other stuff like presentations,
like it just seems like, okay, you just lost how many people out of that, right? Like,
you know, what is the value? Like you say, it's two seconds. One of the things what I would like to see,
and it would be really nice, maybe that would even push me over the edge is it drives me crazy that I
create a new virtual environment from the latest version of Python that I can possibly get on the planet.
And it tells me that pip is out of date. Now virtual environment didn't do that for me this morning.
So it created a virtual environment with the newest pip in it.
Oh, okay. See, now that's pretty nice, because it's annoying to say, okay, what you do is create this virtual
environment, and you pip install this thing. Oh, look, there's always going to be a warning. So every single
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,
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
V and V and V and it does the Python dash M V and V. And then it does an upgrade of pip.
And first it activates it. And then it does an upgrade of pip and setup tools all in like,
four characters. So that's what I've been doing these days.
Yeah, I've got like a little snippet in my profile also that I'm using. Funny enough,
I shared it recently on Twitter, just my like two line snippet that I used. And then people kept on
telling me to use all these other tools. Oh, you could just use this. It's not just use this.
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
is. I just typed these three characters. I'm good. Why are you bothering me? Right? Why is it such a
big deal? I know it's crazy. Yeah. Yeah. What I would really like to see in none of these address
is that something like kind of like node JS, where it just has the virtual environment at the top level
and it just walks up until it finds a virtual environment and maybe complains if it doesn't,
or it does something like that. Right? Like something to the effect where you say here,
I know that this is a feature. I forgot what it's called. It's like the local Python or something like
that, but it's not just built into Python. So if I just went into that directory and tried to run it,
it's not going to find and use that version of Python, you know? Oh, well, the durinv, there's a few,
there are a few packages that do that. And that's one of the things that people are directing me to is
durinv. Durinv is cool. We should talk about it as a separate item. It's worth it. But yeah,
D-I-R-E-N-V is cool. Yeah. Yeah. We should talk about that sometime.
Yeah. Yeah. Yeah. Don't use up all our items all in one show, man. Come on.
Okay. Let's thank our sponsor. So DigitalOcean is sponsoring this episode and DigitalOcean just
launched their virtual private cloud and new trust platform. Together, these make it easier
to architect and run serious business applications with even stronger security and confidence.
The virtual private cloud or VPC allows you to create multiple private networks for your account
or your team instead of having just one private network. DigitalOcean can auto-generate your private
network's IP address range, or you can specify your own. You can now configure droplets to behave as
internet gateways. And Trust Platform is a new microsite provides you one place to get all your
security and privacy questions answered and download their available security certifications.
DigitalOcean is your trusted partner in the cloud. Visit pythonbytes.fm/DigitalOcean
to get a $100 credit for new users to build something awesome.
Yeah. Awesome. Thanks for supporting the show, DigitalOcean. So before you had to wait on me,
didn't you, Brian? It was frustrating.
I did have to wait.
All right. Let me tell you about when you might not even be awaiting stuff and things are still slow.
So there's this cool analysis done by Chris Wellens in an article called Latency in Asynchronous
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.
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.
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.
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.
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?
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?
You just say await asyncio dot whatever, like sleep for one millisecond, then do the thing, and then go on, right?
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.
And then there's this other stuff that has to do some computational work that takes not very long, like 10 milliseconds.
So you're receiving a JSON request, you have to parse that JSON and do it like a little bit of work, right?
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.
It's like there's some variability, but it's no big deal.
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.
Well, the way it works is it all gets scheduled.
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.
And there's a bunch of stuff around them that makes them a little bit slower, the scheduling and whatnot.
And then we have a bunch of more heartbeats.
So it goes beep, beep, block for two seconds.
Beep, beep, beep, where you would expect, okay, I've got all these heartbeats going and I've got 200 little async things.
Let's like mix them up, right?
Like kind of share it fairly and it does not do that at all.
Oh, wow.
So it talks about basically what some of the challenges are there.
One is probably you shouldn't just give it that much work in some giant batch.
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.
Now, I don't remember if semaphores are reentrant or not.
It didn't work.
The semaphore didn't help at all, actually.
He said, we can use a semaphore to limit it to 10.
But if semaphores are reentrant, this is all one thread.
It doesn't matter.
Like the semaphore won't block itself.
So that's like this normal threading, locking and stuff like that.
They kind of don't apply because there's not actual threading going on.
So that doesn't really help.
But he comes up with this example of that the asyncio has a job queue, which allows you to push work into it.
And then you can like wait for it to be completed.
And there's all sorts of cool patterns and like producer consumer stuff that you can put on there.
So I actually put together an example.
He has like little code snippets.
I put together a running example in one whole program that demonstrates this.
And I have a link to the gist in the show notes.
And I also would like to just point out how much a fan of unsync I am, which I always get it.
I always talk about when I can around this async stuff.
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.
It's really, really nice.
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.
That's it.
You still use await and async and await and all that kind of stuff.
The problem is gone.
Totally fixes it.
Oh, really?
Like you don't have to go to like crazy queues and all that.
Like the problem is gone.
It's fixed.
Well, it's alleviated.
It may still be like if you push it far enough under certain like more complex criteria.
But the example that showed the problem, you just make them unsync and you await them.
It just runs like you would have originally expected.
Like unsync is so beautiful.
That's cool.
It doesn't change the way asyncio works.
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.
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.
Anyway, it's pretty interesting.
It's worth a look.
Also, I have a copy of that on the gist and you can check that out and run it too.
That's pretty cool.
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.
Is it?
Yeah, cool.
Yeah, it's really neat.
It just cleans everything up.
But I sure hope they don't deprecate it though.
Oh, that's a better transition.
I was going to say, speaking of the cleaning things up, but...
That works too.
We'll just do both transitions.
So how to deprecate a PyPI package.
So you've put up a PyPI package and for some reason you don't want it to be up there.
I don't want a puppy anymore.
Why do I have to take care of this?
Yeah.
So there's lots of reasons why this might happen.
One of them might just be you accidentally...
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.
Maybe it's some other package that somebody took over and it's handling it better and you want people to use something else.
But anyway, there's lots of reasons why you might.
A guy named Paul McCann wrote a blog post about how to deprecate a PyPI package.
So he gives a few options and I think these are cool.
One of the interesting things that he mentions is that PyPI doesn't really give you direction as to what it should look like.
Which one you should use.
So he's giving his opinion, which is great.
You might use a deprecation warning.
And this doesn't really apply to entire packages.
But let's say you've changed your API.
So it might as well be listed here.
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.
They really should be errors instead of warnings.
If you're really taking them out and just having the warning, something like an assert is probably better.
But there is a good thing to think about whether, don't just rip it out, maybe.
I don't know.
But if you rip it out completely, a assert will happen automatically.
So maybe that's a good thing.
As far as packages, though, you could just delete it.
So you can, PyPI does allow you to remove packages.
I don't think that that's probably the right thing to do, usually, ever.
Unless you just push something up and it was an accident, then deleting it is fine.
But if it's been up there for a while and people are using it,
deleting it has a problem that somebody else could take over the name.
And possibly a malicious package could take over the name and start to have people having install it.
So there's problems with that.
So it's probably not a good choice most of the time.
The last two options are more reasonable.
There's a redirect shim.
So this is an example.
Like, let's say there's an obvious package that is compatible,
that is being better maintained, and you want to push people over to there.
If it's really very compatible, you can add a setup, a shim that just,
and there's some code examples here to just, if somebody installs it,
it just installs the other package also that people should be using.
I know if you want it. We'll give you that one.
Yeah.
And even having, if somebody imports your package,
it really just imports the other package too.
That's a little weird, but it is interesting that it's an option.
The thing I really like that probably the best is just a way to fail during install.
And there's a code example here for if somebody at pip installs something,
and all the packaging works, but the install part will throw an error,
and you can put a message there redirecting people to use a different package,
or maybe just explain why you ripped this one out.
So I think I like the last one best.
So most of those are my commentary.
But there's some options for how to deal with it.
So I thought that was good.
Yeah, I really like the sort of, I tried to pip install it,
and it gives you, instead of just failing or being gone,
it actually gives you a meaningful message.
Like, you should use this other package, we're done.
If you really intend to delete it, that's probably it.
Yeah, and one of the interesting things,
the last couple, the redirect shim and the fail during install,
he gives example packages that do this.
And some of these are just mistyped things.
Like, if people mistype something,
maybe they meant something else, and redirecting there.
Yeah.
It seems so write-only over at PyPI.
And, you know, if you make a mistake, it's not good.
So knowing what to do.
I mean, people depend on it, right?
If you yank it out, then it's trouble.
Yeah, but if it's mistake-driven, though,
make sure you use the test interface first
to play with things before you push garbage up there.
Also, I'd really like people to not squat on names.
There's a lot of cool package names out there
that really have nothing meaningful there
because somebody decided they wanted to grab a name
and then didn't do anything with it.
That's lame.
Don't do that.
Yeah, that's definitely lame.
On the other hand, there are some times I'm like,
how did you just get that name?
There'll be like a new package, like secure or something like that.
Like, how did you get that after all this time, right?
It's crazy.
Yeah, definitely.
Yeah, yeah.
Or up to 236,000 packages.
That's pretty insane.
Yeah.
So, Brian, would you like me to enlighten you a little bit in the listeners?
Yes, please enlighten me.
So, last time you brought up a cool progress bar.
It was either the last time or the time before.
And it did all sorts of cool stuff.
But here's yet another one.
Again, an example of our listeners saying,
oh, here's three cool things you talked about,
but did you know there are these four others you've never heard of?
Yeah.
So, Avram Lubkin sent over his progress bar package called Enlighten.
And it's actually pretty cool.
Like, there's a bunch of cool progress bars with nice animations and stuff.
But there's a few features of Enlighten that might make you choose it.
One is you can have colored progress bars, which is nice.
But more importantly, you can have multicolored progress bars.
So, let me throw out an example that I think would connect for you,
given that you're a fan of pytest.
Like, if you run some sort of series or sequence of operations,
and you want to show how far you're making it,
but they have multiple outcomes, like red is failure, green is success,
and yellow is, like, skip or something like that.
You could have a progress bar that has three segments,
a red segment, a yellow segment, and a green segment.
And it could be all one bar, but it could kind of, like, show you as it grows.
Here's the level of failure.
Here's the level of success, and so on.
All with color, 24-bit color, not just, like, eight colors either.
Oh, yeah.
That'd be great.
That's good.
And then I said, those just go off.
The other one is a lot of these progress bars, they'll sort of control,
they'll be rewriting the screen, right?
They'll be putting stuff across as it's happening.
But if you happen to do, like, a print statement,
effectively writing a standard out or an exception that writes a standard error
or something like that, it, you know, messes them all up, right?
So this one works well, even allows you to write print statements while it's working.
So the print statements kind of flow by above it,
but it's, you know, whatever part of the screen it's taken over,
it still is managing that as well.
So it overrides what print means or stand it out and sends it where it belongs.
That's cool.
Yeah, that's pretty cool.
It also automatically handles resizing, except on Windows.
And where it says except on Windows,
I'm not sure if that means on the new terminal,
Windows terminal that they came out with that's much closer
to what we have over on Mac and Linux,
or if it just means it doesn't work on Windows at all.
I suspect it might work on the new Windows terminal that just went 1.0,
but certainly not on CMD.
Okay.
Who uses CMD?
Okay.
I mean, that's what comes with Windows.
If you don't like go out of the way to get something, right?
Like commander or the new terminal or something like that.
Yeah, but you gotta install Git at least on Windows anyway,
and then you got Bash.
Yeah, that's true.
It comes with it.
That's true.
So like all good things that have actions and behaviors and are visual,
there's a nice little animation even on the PyPI.org page.
So if you go there, you can watch and actually see the stuff scrolling by.
It's like an animated GIF right on the PyPI page.
So very, very nice.
Well done.
You know, the multicolored progress bars,
it does seem pretty awesome if that's your use case.
I want rainbow ones.
I want to do a rainbow.
Yes.
Maybe with like little unicorns just shooting out of it and just like all sorts of crazy.
Yeah.
Sounds good.
Yes.
Stars and unicorns.
Yes.
It would be perfect.
Yeah.
Let's have that.
And people are starting to catch on that we like animations because they'll include it in the suggestion.
And by the way, it has an animation.
Good job.
You can watch it here.
Yeah.
Good job.
Bringing it up.
Yeah.
You're part of it.
Awesome.
Well, nice work on that progress bar library.
It seems simple and well done.
Speaking of unicorns, I want to talk about oceans.
Wait, unicorns don't live in the ocean.
Mermaids.
Let's go with mermaids.
So I want to talk about Code Ocean.
So this was contributed by Daniel Mulkey.
So this is a pretty neat thing.
So Code Ocean is a paid service, but there's a free tier.
And it's a research collaboration platform that supports researchers from the beginning of a project through publication.
So this is kind of this neat thing.
I'm going to read a little bit from their about page.
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.
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.
Anyway, mission is to make research easier.
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.
And it's pretty cool.
It also collaborates with some.
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.
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.
So people trying to do science and be published, associating a Code Ocean space with it is an option.
That's cool.
And if it gets accepted by editors as, yeah, that's what you do, then it just makes it easier.
It's kind of like saying, oh, you have an open source project.
What's the GitHub URL?
Right?
Not is it on GitHub?
Just where on GitHub is it?
Yeah.
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?
Yeah.
Cool.
Well, this looks pretty neat.
I do think there's a lot of interesting takes on reproducibility in science, and that's definitely a good thing.
There's this, there's Binder, which is doing a lot of interesting stuff, although not as focused on exact reproducibility, but still nice.
There's Gigantum, which is also a cool platform for this kind of stuff.
So there's a lot of options, and it's nice to see more of them like Code Ocean.
Yeah.
Nice.
Well, that's R6.
Do you have anything extra for us today?
I'm going to try to connect this to Python because I am excited about it.
How long has it been since astronauts have been launched into space from NASA and from the U.S.?
It's been a really long time, ever since the space shuttle got shut down four or five years ago.
I heard it was like over 10, but I could have heard wrong.
It's very possible.
It's been a very long time.
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.
But I'm super excited for SpaceX's launch in collaboration with NASA to send two astronauts up into space.
Wow.
Are those guys brave to get on to one of these rockets?
But also, I think there's probably somewhere in the mix a lot of Python in action.
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.
Oh, wow.
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.
There could be multiple people into any one role.
So that's a lot of Python.
And so somehow in this launch, there's got to be some interesting stories around Python.
And this is mostly to say, one, it's awesome that SpaceX and NASA are doing this.
Hopefully this goes well.
Lots and lots of luck to that.
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.
We would love to hear about those and introductions and whatnot.
Yeah, that would be cool.
I'd love to hear more about that.
Yeah.
I do hope it goes well.
And I heard that possibly there was weather problems that might crop up, but we'll...
Well, maybe people will get to watch this.
Knock on wood.
Maybe it'll be delayed for a week.
We'll see.
Awesome.
How about you?
What do you got?
I just downloaded 3.90 beta 1.
So Python 3.9 beta 1 is available for testing.
If you are maintaining a package or any other maintaining your application, you probably ought to download
and make sure your stuff works with 3.9.
Oh, yeah.
That's cool.
And because it's a beta now, it should be frozen in terms of features and APIs and stuff, right?
It's no longer changing.
So it's now time to start making sure your stuff works and yelling if it doesn't.
Yeah.
Right.
And another reason to download it is the prompt, VNV with the prompt with the magic dot that turns your directory name.
Yeah.
That is in 3.9.
Yeah.
Super cool.
Awesome.
Well, that's not very funny, but I could tell you something that is.
And it's very relevant to your item here, actually.
Okay.
You ready for this?
So open up this link here and I'll put the link in the show notes because this is a visual.
I got to describe it to you.
So this was sent over by Stephen Howell.
Thank you for that.
And this would be better during Halloween, but Halloween is far away.
So we're going to do it this way.
So there's a person standing around and there's a ghost standing behind them, right?
Yeah.
And the ghost says, boo.
Person doesn't react.
Boo.
Person doesn't react.
Boo.
Person doesn't react.
The person says, Python 2.7.
Ah!
The person runs away.
Yeah, this is great.
It's good, right?
Yeah.
You got it as well.
What do you got here?
Well, I'm going to get haters for this, but I'm going to say it anyway.
So somebody named Bert sent us a meta joke because we have used PyJokes before.
We love PyJokes.
And I'm going to modify it a little bit.
So what does PyJokes have in common with Java?
It gets updated all the time, but never gets any better.
That's pretty funny.
I don't even really use Java, but I have a Java tool on my desktop.
And so I get like, Java's updated.
Do you want to do the update?
All the time.
Make it stop.
Yeah.
Make it stop.
Yeah, that's funny.
Yeah, PyJokes is good.
If you all need some programming jokes, just pip install --user PyJokes.
And then you can type PyJoke anytime you want.
Yeah.
I had to change it because the original joke was like about Flash, Adobe Flash.
And who has that?
Is that even a thing anymore?
Yeah.
I don't even think it gets updated anymore.
I don't know.
Maybe it does.
I sure hope it's not on my computer.
Yeah.
It's a security phone.
Yeah, it totally is.
Awesome.
All right.
Well, very funny.
All right.
Thank you.
Yep.
You bet.
Bye-bye.
Thank you for listening to Python Bytes.
Follow the show on Twitter at Python Bytes.
That's Python Bytes as in B-Y-T-E-S.
And get the full show notes at Python Bytes.fm.
If you have a news item you want featured, just visit Python Bytes.fm and send it our way.
We're always on the lookout for sharing something cool.
This is Brian Okken.
And on behalf of myself and Michael Kennedy, thank you for listening and sharing this podcast
with your friends and colleagues.