WEBVTT

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

00:00:04.660 --> 00:00:12.180
your earbuds. This is episode 165, recorded January 16th, 2020. I'm Michael Kennedy.

00:00:12.180 --> 00:00:13.120
And I'm Brian Okken.

00:00:13.120 --> 00:00:17.120
And this episode is brought to you by DigitalOcean. They're a great support of the show.

00:00:17.120 --> 00:00:21.920
Check them out at pythonbytes.fm/digitalocean. Get $100 credit for new users.

00:00:21.920 --> 00:00:26.460
More on that later. Brian, we've got a lot of stuff to get through and I want to just,

00:00:26.460 --> 00:00:27.640
let's start iterating through it, man.

00:00:27.640 --> 00:00:31.740
Okay, let's iterate through it. Also, I can't believe that it's halfway through January,

00:00:31.740 --> 00:00:38.400
whatever. Okay, so first off, let's talk about iterators, iterators, generators, and coroutines.

00:00:38.400 --> 00:00:44.380
So I'm linking to an article, that's pretty much what it's called, by Mark McDonald. And

00:00:44.380 --> 00:00:50.560
when I googled this relationship between coroutines and generators, apparently everybody else knows

00:00:50.560 --> 00:00:55.180
this is a thing, but I missed out somehow. But this article is a really good introduction

00:00:55.180 --> 00:00:58.620
to all of this concept and how they all work together.

00:00:58.620 --> 00:01:04.680
So it starts, well, okay, I've got to start out with a beef. It starts out with like talking,

00:01:04.680 --> 00:01:11.240
trying to do a gentle introduction to the iterator protocol with like the dunder iter and dunder next.

00:01:11.240 --> 00:01:16.920
I just want people to stop doing that. Okay, muscle through it, but skip that part. It should be an

00:01:16.920 --> 00:01:22.900
appendix, I think, because people don't do that anymore. Okay, next, it goes talks about generators,

00:01:22.900 --> 00:01:29.540
which are the same thing as this iterator protocol, sort of, but using the yield function. I know there's

00:01:29.540 --> 00:01:33.100
differences, but this is how I do it. I use yields for generators.

00:01:33.300 --> 00:01:39.120
It's so beautiful, because you take the code that's not generator style, and then you just throw in yield

00:01:39.120 --> 00:01:44.700
instead of like list append, or set.add, or whatever you're going to do to gather up the results. Just

00:01:44.700 --> 00:01:49.980
replace that with yield, boom, you're done. It's usually less code. I love it. It's great. I'm a big fan.

00:01:49.980 --> 00:01:55.080
Like, for instance, you just do throw things into a loop and put yield in there, or yield the things

00:01:55.080 --> 00:02:00.740
you have, whatever it works. Unbound generators, it talks about, which means don't convert these to

00:02:00.740 --> 00:02:05.400
lists because they don't stop. So there are, it is possible to write a for loop that doesn't stop,

00:02:05.400 --> 00:02:09.500
and therefore there's a way to do a generator that doesn't stop. So.

00:02:09.500 --> 00:02:14.240
Right, if you're working on infinite series, some kind of series that you use a generator for it,

00:02:14.240 --> 00:02:18.800
it might not stop. Yeah, there's, I mean, there's legitimate reasons to do this, or maybe it does have

00:02:18.800 --> 00:02:24.200
an end, but it doesn't fit in memory and stuff like that. So beware. Generator expressions, you know,

00:02:24.200 --> 00:02:30.160
for some reason I just forget about. They're like list comprehensions, but you put parentheses instead

00:02:30.160 --> 00:02:35.320
of brackets, and then it's a generator expression? They're smooth, right? I mean, they don't have those

00:02:35.320 --> 00:02:42.120
sharp edges of those square braces. Smooth? Oh, wow. That was bad. Okay. The reason why I highlighted

00:02:42.120 --> 00:02:48.680
this article really isn't for this stuff so far. It's a couple things. It talks about the generator

00:02:48.680 --> 00:02:55.060
generators can use other generators or nesting generators with a yield from, and this is cool.

00:02:55.060 --> 00:03:00.720
I didn't know this was a thing so that you can have, so let's say bar and baz are, are generators.

00:03:00.720 --> 00:03:07.120
You can define a new function foo that yields from each of these, and it just goes through one.

00:03:07.120 --> 00:03:11.880
And then when it's exhausted, it goes through the other really slick. Did you know this was a thing?

00:03:11.880 --> 00:03:17.200
Yeah, this was added after the yield keyboard was added. So yield was there for a while.

00:03:17.200 --> 00:03:22.180
And then what you would have to do before if you wanted one of these, you'd have to write a for loop

00:03:22.180 --> 00:03:27.000
that goes through every item in the sub generator and then just yield that out. But now you can just

00:03:27.000 --> 00:03:31.200
say yield from that thing. It's been a few versions that it came in. I can't remember exactly when,

00:03:31.200 --> 00:03:36.060
but yeah, it's a bit of a new feature. Maybe three, five, maybe three, four. I can't remember,

00:03:36.200 --> 00:03:43.200
but yeah, this is great. The place that I've used this most is recursive generators, right? You're

00:03:43.200 --> 00:03:47.600
writing a generator and it's going through some data structure, but then you get to the point where

00:03:47.600 --> 00:03:51.180
you're like, well, I need to call it again, but with a different node in a tree or something like

00:03:51.180 --> 00:03:55.400
that. Instead of having to loop over that deal, you just say yield from basically the recursive call.

00:03:55.460 --> 00:04:00.520
Oh, yield from with a recursive call. Nice. That hurts my head thinking.

00:04:00.520 --> 00:04:05.460
Yeah, man. Think about, you know how painful it was to learn recursion and how funky it is to

00:04:05.460 --> 00:04:10.940
learn about generators. You like mash them together and then the brain explodes. Yeah, it's great.

00:04:11.020 --> 00:04:15.420
Okay. The article goes on and talks about the relationship between coroutines and generators

00:04:15.420 --> 00:04:21.980
because yield usually just has, it's just a thing that it ends up being returning a value out of your

00:04:21.980 --> 00:04:30.800
function, but you can equal your, to an assignment, a variable assignment from a yield. And that's one of

00:04:30.800 --> 00:04:37.800
the syntax things that works with coroutines. And I got to admit, I got lost at this point. So this is

00:04:37.800 --> 00:04:42.800
kind of a call to action to everybody. I'd really like to have a coroutine tutorial that could show

00:04:42.800 --> 00:04:49.380
me how to use coroutines for stuff that I really actually might use that isn't async related. And

00:04:49.380 --> 00:04:55.660
can we skip the iterator protocol or make it an appendix? Like you said. Yeah. Do you use coroutines?

00:04:55.660 --> 00:05:00.980
I mean, they look neat. I just don't know how to use them. I use generators all the time and I use

00:05:00.980 --> 00:05:07.780
async methods, which ultimately are fancy wrappers around coroutines, but I don't use coroutines

00:05:07.780 --> 00:05:12.740
coroutines directly. Not knowingly anyway. Okay. Cool. Yeah. I'll have to play with it a little bit.

00:05:12.740 --> 00:05:17.440
Yeah. Nice. Something that I use a lot is requests. You probably use requests a lot as well.

00:05:17.440 --> 00:05:23.360
Yeah. Lots of people do. Yeah. And requests is one of these things, you know, last time you spoke about

00:05:23.360 --> 00:05:29.600
PyPI stats, was it pypistats.org or something like that. And requests was certainly right near the top.

00:05:29.600 --> 00:05:34.080
It was not number one on the list of things being used, but it was near the top and which that means it's,

00:05:34.140 --> 00:05:40.740
it can't take too much change, right? There can't be too many features or changes made to it. So it would

00:05:40.740 --> 00:05:45.740
be nice to have something that makes working with requests nicer that can change more quickly. So

00:05:45.740 --> 00:05:51.640
there's this thing that I came across called request tool belt. Yeah. So request tool belt is a,

00:05:51.640 --> 00:05:57.140
well, tool belt of useful classes and functions to use, to like make working with requests easier.

00:05:57.480 --> 00:06:02.280
And it really does at the moment for things. But I think if people are out there and they're like,

00:06:02.280 --> 00:06:06.360
I always have to do this with requests, it's like these five lines. I got to make sure I remember to

00:06:06.360 --> 00:06:11.500
do this right. It would be awesome to just, you know, extend this. So this is a small project by

00:06:11.500 --> 00:06:16.760
someone I can't remember. I don't think it says like really a meaningful name on it. Yeah. No,

00:06:16.760 --> 00:06:20.040
it's just under requests. Actually, this is not the small project I think, you know, but I think it

00:06:20.040 --> 00:06:24.960
would be cool to like take those ideas. If you see patterns that you're doing with the request library and

00:06:24.960 --> 00:06:29.380
fold them in here. So let me give you the rundown on the four things it does. First of all, if you're

00:06:29.380 --> 00:06:37.360
going to do multi-port form data encoding, like I have an image file and I want to upload it to the

00:06:37.360 --> 00:06:43.540
server, to the API, that's annoying, right? It's not, not super easy, but with this thing, it's really

00:06:43.540 --> 00:06:50.140
easy to go and just basically say, here's a file stream that is field two. It's, you know, whatever

00:06:50.140 --> 00:06:55.420
it is, right? It's binary image data or it's text. And then you just say, here's my data,

00:06:55.420 --> 00:07:01.500
this multi-part form encoder and boom, it's just uploading files and doing all the stuff it has to

00:07:01.500 --> 00:07:04.780
do. That's incredible. Just a few lines of code. Yeah. It's really, really nice. And you don't have

00:07:04.780 --> 00:07:08.740
to think about like, how do I do multi-part encoding again? Just give it a file stream. You're good.

00:07:08.740 --> 00:07:14.300
The next one is the user agent constructor. So you have to set a header user to ask agent,

00:07:14.420 --> 00:07:17.980
but then like, how do you construct that in a meaningful way? There's a class that takes,

00:07:17.980 --> 00:07:22.620
or a method, I think it's just a method, takes some arguments and it will generate the string

00:07:22.620 --> 00:07:30.120
that is a, I guess, compliant user agent for like your API app or whatever. So that's cool. User

00:07:30.120 --> 00:07:36.640
agent constructor. Sometimes you have to, when you're working with other systems, conform to certain

00:07:36.640 --> 00:07:43.600
SSL protocols, right? We have TLS version one, 1.2. We have two, I think coming along,

00:07:43.600 --> 00:07:48.960
but there's different versions of TLS, which is the foundation of SSL, right? So they have an SSL

00:07:48.960 --> 00:07:55.640
adapter that lets you explicitly set, I want to use TLS 1.2 or 1.0 or something like that if you need

00:07:55.640 --> 00:08:00.860
to. Oh, wow. Okay. That's cool. And then one thing that you can do with requests is you can create a

00:08:00.860 --> 00:08:06.400
session and then it'll start talking over it. It probably reuses the connection. I'm not entirely sure

00:08:06.400 --> 00:08:10.820
of all the things it does, but one of the things the session does is it'll remember cookies and things like

00:08:10.820 --> 00:08:17.380
that. Well, maybe you want to make a series of requests using a request session that doesn't

00:08:17.380 --> 00:08:23.200
actually carry the cookies from time from request one to two to three and so on. So one of the classes

00:08:23.200 --> 00:08:31.340
in here is a forgetful cookie jar. So if you, if you set the request session cookies container to the

00:08:31.340 --> 00:08:36.300
forgetful cookie jar, it will, well, it implements the protocol, but it always forgets its cookies,

00:08:36.300 --> 00:08:41.400
obviously. So it's a cool way to like clear out still use sessions, but clear out cookie

00:08:41.400 --> 00:08:47.260
persistence across calls. Is there a reason to use sessions without cookies? Well, some websites

00:08:47.260 --> 00:08:52.520
behave differently if they think they've already seen you or things like that, right? Yeah. Like maybe I

00:08:52.520 --> 00:08:58.900
want to test the login function, both working and not working. And then I want to try it of, I forgot my

00:08:58.900 --> 00:09:04.000
password, but it, I don't want it to know that I've already actually logged in and that sequence or

00:09:04.000 --> 00:09:08.780
something like it could be some like series that you're testing for playing with. Okay. So like,

00:09:08.780 --> 00:09:13.900
if you got a login, your session login is still valid, but you have to go. Yeah. Yeah. Or maybe

00:09:13.900 --> 00:09:18.900
you're going to a place like some sort of paywalled ad place and it's like, well, you can come here

00:09:18.900 --> 00:09:23.480
three times, but if you come here more than three times this month, we're going to show you the paywall.

00:09:23.480 --> 00:09:27.800
You know what I mean? You're like, well, you're using cookies for that. And my cookie jar is forgetful.

00:09:27.960 --> 00:09:32.860
I don't know. There's, I don't personally have a reason for it, but I can imagine reasons that that

00:09:32.860 --> 00:09:36.800
might, people might use that for like automation and whatnot. I predict that we will hear other

00:09:36.800 --> 00:09:42.320
people telling us the reasons now. Yeah, absolutely. They definitely might. So people can visit

00:09:42.320 --> 00:09:47.460
Pythonbytes.fm slash 165 and down at the bottom, they can tell us why, why they're doing some cool

00:09:47.460 --> 00:09:51.100
comment section. Yeah. All right. Speaking of cool, let me tell you about DigitalOcean. They're doing all

00:09:51.100 --> 00:09:56.500
sorts of good stuff. They're offering a hundred dollars credit for new users. So it was 50,

00:09:56.500 --> 00:10:02.160
it's back to a hundred. Yay. That's great. And we, all of our infrastructure and stuff runs on DigitalOcean

00:10:02.160 --> 00:10:07.480
and it's been just perfect for years. So that's great. One of the things they recently released is

00:10:07.480 --> 00:10:14.520
memory heavy workload droplets. So memory focused droplets. So you can get up to eight gigs of RAM for

00:10:14.520 --> 00:10:24.380
each dedicated CPU and it goes from two CPUs all the way up to, is that 32, 256 gigs of RAM available

00:10:24.380 --> 00:10:28.520
on your VM, which is kind of ridiculous if you really need that, but you know, maybe you've got

00:10:28.520 --> 00:10:33.780
a workload that does. So it's really good for high memory apps, like a high performance SQL or no SQL

00:10:33.780 --> 00:10:39.720
databases and memory caches like Redis, maybe some data analysis of lots of data, stuff like that. So

00:10:39.720 --> 00:10:44.160
check them out at pythonbytes.fm/DigitalOcean, get a hundred dollars credit from new users and

00:10:44.160 --> 00:10:48.500
support the show. Speaking of data science, what do you got, Brian? What's next? Yeah. Speaking of data

00:10:48.500 --> 00:10:54.420
science, pandas is used by lots of folks, not just data science, but I know the data analysis people use

00:10:54.420 --> 00:11:01.820
pandas quite a bit. And in episode 162, you weren't with us for that, but we covered a project called

00:11:01.820 --> 00:11:06.540
Bulwark. Yeah. I listened into that episode as well and you and Ollie did a great job. That was fun.

00:11:06.540 --> 00:11:13.920
And we had a listener suggestion about another package called pandas validation. And then I was

00:11:13.920 --> 00:11:20.020
just looking around to see if there's other projects. One of the others I've found was Pandera. So I'll

00:11:20.020 --> 00:11:26.320
try to briefly talk about these, but pandas validation, Lance tells us that it lets you create a template

00:11:26.320 --> 00:11:33.020
for your data frame, how it should look, and then it validates your entire data frame against a template.

00:11:33.020 --> 00:11:38.820
So if you have a data frame with the first column being string and second column being dates and then

00:11:38.820 --> 00:11:44.880
an address, and you can use a mixture of built-in validate types to ensure that your data conforms

00:11:44.880 --> 00:11:49.340
to that. So that looks pretty cool. Yeah, this is really nice. It's a little bit like tiny bit like

00:11:49.340 --> 00:11:54.920
JSON schema or something. So you've got these pandas data frames or time series that it's just full of

00:11:54.920 --> 00:11:59.780
whatever. And then you can throw on top of it a cool validation and just it's all at once against the

00:11:59.780 --> 00:12:05.160
whole collection, right? Yeah. And then Pandera is, I think, a similar sort of project that lets

00:12:05.160 --> 00:12:11.960
you set up types and properties for different columns of a data frame and perform validation

00:12:11.960 --> 00:12:17.000
to make sure sort of a schema validation sort of thing also. So they're all kind of solving a similar

00:12:17.000 --> 00:12:23.080
problem, but I was looking at it and the API and how you use it between Bulwark, Pandas validation,

00:12:23.260 --> 00:12:28.680
Pandera are all very different. Yeah, they are. I'd really like to hear if there is a common approach

00:12:28.680 --> 00:12:34.580
or if Pandas validation, data frame validation is just not something that's catching on yet or,

00:12:34.580 --> 00:12:38.780
you know, what people are using. I'd love to hear that. Yeah. And I just noticed at the bottom of

00:12:38.780 --> 00:12:45.140
Pandera, they have other data validation libraries and others, Panda specific ones like opulent Pandas

00:12:45.140 --> 00:12:49.820
and Panda schema and Pandas validator and table enforcer and so on. So apparently this is like a whole

00:12:49.820 --> 00:12:55.720
hole you can go down into that I was not even aware of, but I got to say the Pandera API where

00:12:55.720 --> 00:13:02.540
you basically define a column, a data type, and then a Lambda function that you give it that does

00:13:02.540 --> 00:13:07.220
the validation. That's super cool. I love that. Yeah. It looks pretty clean. Yeah. It looks incredibly

00:13:07.220 --> 00:13:11.500
flexible without getting like out of control. Yeah. Speaking of out of control, you know, it's a

00:13:11.500 --> 00:13:18.980
little bit out of control. GUIs for Python. Yeah. And this way, I don't mean it. I'm not actually this time

00:13:18.980 --> 00:13:27.180
complaining about their absence or something like that. But one of the best libraries for building GUIs

00:13:27.180 --> 00:13:37.500
in Python has got to be Qt, right? Yeah. And I was inspired at the Python meetup that you're running out in

00:13:37.500 --> 00:13:43.180
West Portland when we saw Augie Moore give a presentation and how he used FBS, Fman build

00:13:43.180 --> 00:13:51.460
system, plus PyInstaller, plus Qt to build, you know, nice packaged apps that are GUI apps that he

00:13:51.460 --> 00:13:55.660
could distribute around. And that was really cool. So one of the things though that drives me crazy is

00:13:55.660 --> 00:14:02.940
like we've got PyQt 5, we've got PySide 2, PyQt 4, we have PySide, we have Python 4 Qt. We have all

00:14:02.940 --> 00:14:09.240
these different, different things, right? I think Python 4 Qt might be the next version of PyQt 5 and so on.

00:14:09.240 --> 00:14:13.960
And I just don't know where to start, right? And look at it, that's going, oh my goodness. Like you see

00:14:13.960 --> 00:14:20.680
different examples doing different things. And so I ran across something called QtPy. QtPy.

00:14:20.680 --> 00:14:22.740
QtPy. Yeah. Or QtPy.

00:14:22.880 --> 00:14:32.400
I wanted to say QtPy, but I don't know. QtPy. QtPy. So QtPy, actually, you know, one of the things

00:14:32.400 --> 00:14:36.520
about a lot of these libraries is they're like really cool little proof of concepts, but in practice,

00:14:36.520 --> 00:14:44.120
how real are they? How supported are they? And so on. One thing that seems real and supported is

00:14:44.120 --> 00:14:49.680
Anaconda, the Anaconda distribution. And with that comes the spider IDE, like the whole Anaconda

00:14:49.680 --> 00:14:58.460
Continuum data science IDE thing, right? And this QtPy is the foundation of what they're doing to

00:14:58.460 --> 00:15:04.760
write that. Okay. At least it's in their GitHub repo. So it provides a uniform layer to support all

00:15:04.760 --> 00:15:10.200
those different libraries that I complained about with a single uniform API. So it's like an adaptive

00:15:10.200 --> 00:15:14.700
layer on top of all those things. And it figures out what version you're actually running against.

00:15:14.700 --> 00:15:18.380
And then it just adapts. So you write a code once and then you can run it in all these different

00:15:18.380 --> 00:15:22.360
ways or against these different examples. Yeah, it's nice. Yeah, it's cool, right? So this is

00:15:22.360 --> 00:15:26.560
created by the spider development team. And there's not a whole lot to it. Basically, it's like,

00:15:26.560 --> 00:15:31.400
well, here's a simple simpler way to work with these different libraries, because maybe you want a

00:15:31.400 --> 00:15:38.020
different license or you want to go from PyQt4 to PyQt5 or something like that. Because there's

00:15:38.020 --> 00:15:41.700
all these different examples built with all these different libraries, and they're not exactly

00:15:41.700 --> 00:15:46.660
compatible. So quite cool, I think. Yeah. And also during the presentation at the meetup,

00:15:46.660 --> 00:15:53.000
Is it Ogie? Yeah. Mentioned that just he uses that. And then if there's a problem with one of

00:15:53.000 --> 00:15:58.640
these packages, just uninstall it and install one of the other ones. And you don't have to change your

00:15:58.640 --> 00:16:03.320
code at all. Yeah, it's cool. It just works. One of the other things I thought was neat is at the

00:16:03.320 --> 00:16:09.300
bottom of the readme, they've got sponsors like, you know, different sponsors at the bottom and become

00:16:09.300 --> 00:16:13.720
a sponsor. I have not seen an open source project do that before. It's an interesting idea.

00:16:13.720 --> 00:16:16.900
Yeah, it is definitely an interesting idea. I haven't seen that either.

00:16:16.900 --> 00:16:20.900
Yeah. So maybe I'll try that on my little open source project.

00:16:20.900 --> 00:16:24.900
Well, they also have the GitHub sponsor at the top. Are you using the GitHub sponsor?

00:16:24.900 --> 00:16:25.380
No.

00:16:25.380 --> 00:16:28.660
That's something people can turn on. I think that's really cool that GitHub did that,

00:16:28.660 --> 00:16:34.200
that people can now sponsor projects like through GitHub instead of negotiating some

00:16:34.200 --> 00:16:35.520
deals separately with everyone.

00:16:35.520 --> 00:16:39.020
Yeah. I wonder if they're tied together. Oh, I did look into it. Anyway.

00:16:39.120 --> 00:16:42.460
Yeah. Check it out. All right. So yeah, what's next?

00:16:42.460 --> 00:16:45.480
Well, I want to shed some light on spreadsheets.

00:16:45.480 --> 00:16:51.760
They can be a dark place if you get sucked down into VBA or too far down there.

00:16:51.760 --> 00:16:56.300
Yeah. So actually we got an email from Victor Kiss. I think it's Victor Kiss.

00:16:56.300 --> 00:17:03.100
K-I-S. He said he's got his, his very first open source project, but it looks darn cool.

00:17:03.100 --> 00:17:11.620
It's called PyLite XL and it's an XLS spreadsheet thing that you can read and write spreadsheets with it.

00:17:11.680 --> 00:17:19.800
So it's a lightweight, zero dependency, minimal functionality, read writer has other than the standard library.

00:17:19.800 --> 00:17:26.820
There's no outside dependencies and you can read and write modern XLSX and XLSM files.

00:17:26.820 --> 00:17:34.240
And with a very simple interface for getting access to the different sheets inside there and rows and columns and stuff.

00:17:34.240 --> 00:17:36.440
Actually, it's pretty, looks pretty cool.

00:17:36.660 --> 00:17:40.000
Yeah. It looks totally useful if all you got to do is like get in there and get the data.

00:17:40.000 --> 00:17:56.120
I don't know if it like does things like lets you change say like conditional formatting or other weirdness, but it definitely, if you just get, want to open up an Excel worksheet, not a CSV, but like a full on XLS and get at the data or the rows or whatever it is you're after.

00:17:56.120 --> 00:17:56.900
It's quite neat.

00:17:57.020 --> 00:18:03.880
If you go to the link that you're linking to and just scroll down a bit, there's a little animated GIF and I think it tells you pretty much all you need to know.

00:18:03.880 --> 00:18:08.100
You just watch it for a second and it's like, here's the few steps to go work with this Excel file.

00:18:08.100 --> 00:18:08.400
It's cool.

00:18:08.400 --> 00:18:20.620
He's already got documentation up with the API, but I found the most on the docs, the best way also to get up to speed really quick is to look at his, he's got a handful of examples for how to do different things.

00:18:20.620 --> 00:18:28.460
And it's like, oh my gosh, I could just, if I needed to get read Excel from Python, I could get started in a few minutes with this.

00:18:28.460 --> 00:18:31.040
So yeah, it looks pretty cool.

00:18:31.780 --> 00:18:32.180
Yep.

00:18:32.180 --> 00:18:32.900
And no dependencies.

00:18:32.900 --> 00:18:34.280
That's kind of nice as well.

00:18:34.280 --> 00:18:52.060
So I never really thought about why that would be important, but he lists one of the reasons is that if you are going to a few things, if you're going to compile it into another installer or something using pi installer, not having any DLL or other dependencies makes this easier.

00:18:52.060 --> 00:18:56.760
And then he even says that he's a, the library is just like a few source files.

00:18:56.760 --> 00:19:03.440
So if you don't even want to install this as a package, if you just want to copy this stuff into your own source, that that's an option.

00:19:03.440 --> 00:19:04.140
Right.

00:19:04.140 --> 00:19:04.420
Yeah.

00:19:04.420 --> 00:19:05.140
Just vendor it.

00:19:05.140 --> 00:19:06.940
And then you, you don't have dependencies either.

00:19:06.940 --> 00:19:07.600
Yeah.

00:19:07.600 --> 00:19:09.700
You know, getting updates, but you know.

00:19:09.700 --> 00:19:09.960
Yeah.

00:19:09.960 --> 00:19:10.320
Wow.

00:19:10.320 --> 00:19:11.680
It's a trade-off.

00:19:11.680 --> 00:19:14.820
I'm going to tell you about this other thing.

00:19:14.820 --> 00:19:18.440
And at first it might not sound very exciting, but I'm actually pretty excited about it.

00:19:18.440 --> 00:19:20.360
I think this is, this is quite cool.

00:19:20.360 --> 00:19:21.800
It's a clever little library.

00:19:22.340 --> 00:19:25.780
And this suggestion comes to us from Aiden Price.

00:19:25.780 --> 00:19:31.380
And he told us about some project he's working on using something called Python dash ranges.

00:19:31.380 --> 00:19:31.920
Okay.

00:19:31.920 --> 00:19:32.400
Okay.

00:19:32.400 --> 00:19:35.520
So we have range, like the built-in range.

00:19:35.520 --> 00:19:38.540
You can say, you know, start equals whatever, end equals whatever.

00:19:38.540 --> 00:19:44.160
And it goes from the start integer wise up to, but not including the upper bound.

00:19:44.160 --> 00:19:48.700
But you can't use that range in like more meaningful ways.

00:19:48.820 --> 00:19:55.680
So for example, if I had a range of zero to a hundred, I can't easily ask, is X in there,

00:19:55.680 --> 00:19:55.940
right?

00:19:55.940 --> 00:20:01.140
If X is a number or if I have two ranges and I want to intersect them, how do I do that?

00:20:01.140 --> 00:20:07.760
But this library takes that kind of basic idea, sort of like series, but with a lot of set

00:20:07.760 --> 00:20:10.200
operations, you can ask for the intersection of ranges.

00:20:10.200 --> 00:20:14.500
You can ask for whether or not they're mutually exclusive, things like that.

00:20:14.560 --> 00:20:18.800
So all the set operations you can do on it, but then it also extends that.

00:20:18.800 --> 00:20:25.100
So you can have a range set, which is a bunch of different ranges or even a range dictionary.

00:20:25.100 --> 00:20:27.860
So like, why would you care about that?

00:20:27.860 --> 00:20:32.240
So what you could do with a range dictionary is you can use ranges as keys.

00:20:32.240 --> 00:20:34.620
So if the example they give is if you have...

00:20:34.620 --> 00:20:34.640
That's crazy.

00:20:35.240 --> 00:20:37.520
I know, but here's the example they give.

00:20:37.520 --> 00:20:41.760
It's probably abusing the concept of a dictionary, but it's really useful.

00:20:41.760 --> 00:20:46.920
So if you had an if statement that said, if they use tax or something like that, let's just

00:20:46.920 --> 00:20:50.420
say, if your income is zero to 10,000, you're in bracket A.

00:20:50.420 --> 00:20:55.760
If you're in 10,001 to 20,000, you're in bracket B and so on.

00:20:55.760 --> 00:20:59.480
And you had like a huge if, else if, else if, else if, else if to test for that condition.

00:20:59.480 --> 00:21:06.520
You can create a range dictionary where the key is a range zero to 10,000, 10,001 to 20,000

00:21:06.520 --> 00:21:07.020
and so on.

00:21:07.020 --> 00:21:09.400
And then like some information about it is the value.

00:21:09.400 --> 00:21:15.100
And then you could just take a number like 37,215 and get it from the dictionary.

00:21:15.100 --> 00:21:18.380
Say, I want to get that from the dictionary and it'll return.

00:21:18.760 --> 00:21:25.940
So it'll basically do the test, like is this item in this range as part of the key match

00:21:25.940 --> 00:21:26.520
of a dictionary?

00:21:26.520 --> 00:21:27.740
That's brilliant.

00:21:27.740 --> 00:21:28.460
That's cool.

00:21:28.460 --> 00:21:28.600
Isn't that cool?

00:21:28.600 --> 00:21:29.360
Isn't that cool?

00:21:29.360 --> 00:21:32.240
It's got to be abusing the idea of the dictionary, really.

00:21:32.240 --> 00:21:33.200
But it's pretty cool.

00:21:33.200 --> 00:21:33.540
Yeah.

00:21:33.540 --> 00:21:33.860
Yeah.

00:21:33.860 --> 00:21:37.140
So it's almost like a switch statement in a sense.

00:21:37.140 --> 00:21:40.900
Like you could take those things and those that if, else, and replace it with this just

00:21:40.900 --> 00:21:45.680
flat statement of these ranges, and then it'll do the comparison kind of in the data structure.

00:21:45.680 --> 00:21:46.240
Yeah.

00:21:46.240 --> 00:21:47.520
Sweet.

00:21:48.080 --> 00:21:51.160
So there's a bunch of stuff that you can do with these ideas.

00:21:51.160 --> 00:21:55.800
They got some good examples, but that little example I gave you, I think is probably the

00:21:55.800 --> 00:21:59.480
simplest one to tell you about because it gives you a good sense of like why you might actually

00:21:59.480 --> 00:22:00.380
use this, right?

00:22:00.380 --> 00:22:05.180
Like a lot of times you look for these blocks or these ranges and it's really cool to figure

00:22:05.180 --> 00:22:07.420
out, to be able to sort of test in here.

00:22:07.420 --> 00:22:10.060
Like you could even do really interesting stuff.

00:22:10.060 --> 00:22:15.380
Like I want to know is this thing in any of these five ranges, you could just create one

00:22:15.380 --> 00:22:20.260
of these range sets or these range dictionaries and just ask, is this number in this set?

00:22:20.260 --> 00:22:23.520
If it is, it's in one of the five ranges that are in there.

00:22:23.520 --> 00:22:25.680
There's really like cool ways to layer these together.

00:22:25.680 --> 00:22:25.920
Yeah.

00:22:25.920 --> 00:22:27.760
And especially if you've got that all over the place.

00:22:27.760 --> 00:22:30.860
Like for instance, I'm thinking hardware stuff.

00:22:31.040 --> 00:22:31.260
Yeah.

00:22:31.260 --> 00:22:32.020
It's got to be in there.

00:22:32.020 --> 00:22:34.340
There's a bunch of numbers and frequencies and whatnot, right?

00:22:34.340 --> 00:22:34.660
Right.

00:22:34.660 --> 00:22:39.200
So if I've got different power levels, for instance, they'll have different attenuators that'll

00:22:39.200 --> 00:22:43.580
kick in at different power levels, but I don't want those power level numbers to be hard

00:22:43.580 --> 00:22:44.800
coded all over my code.

00:22:44.800 --> 00:22:50.800
So having some central place where I put those in place so that I can just throw in a number

00:22:50.800 --> 00:22:52.220
and it gets based on that.

00:22:52.340 --> 00:22:53.940
I know what the attenuation is or something.

00:22:53.940 --> 00:22:54.580
That'd be great.

00:22:54.580 --> 00:22:55.360
It's cool.

00:22:55.360 --> 00:22:55.600
Yeah.

00:22:55.600 --> 00:22:56.100
That's cool.

00:22:56.100 --> 00:22:59.980
It also, it occurs to me, this might be useful for testing, right?

00:22:59.980 --> 00:23:04.140
Because then your assert statement could have a little bit of ambiguity, right?

00:23:04.140 --> 00:23:06.720
If there's like, well, as long as it's in this range, it's okay.

00:23:06.720 --> 00:23:08.020
But if it's not, then it's not.

00:23:08.020 --> 00:23:11.240
And so maybe that's also an interesting way to simplify testing.

00:23:11.240 --> 00:23:11.700
Yeah.

00:23:11.700 --> 00:23:12.140
Yeah.

00:23:12.140 --> 00:23:12.560
Okay.

00:23:12.560 --> 00:23:12.940
Cool.

00:23:12.940 --> 00:23:13.220
Cool.

00:23:13.220 --> 00:23:17.540
Well, anyway, I think that's a much more interesting project than it just sort of sounds like.

00:23:17.540 --> 00:23:19.280
It's like, well, Python has range built in, whatever.

00:23:19.280 --> 00:23:20.520
But no, this is cool.

00:23:20.520 --> 00:23:20.860
Yeah.

00:23:21.080 --> 00:23:22.440
Yeah, that's it for our main item.

00:23:22.440 --> 00:23:24.080
So what else do you want to tell folks about?

00:23:24.080 --> 00:23:26.600
Well, I spent some time last night.

00:23:26.600 --> 00:23:31.580
I think I brought this up, I don't know, last time or the time before, that I have a few

00:23:31.580 --> 00:23:37.680
open source projects, not many, but one of them was lacking some work because it had a

00:23:37.680 --> 00:23:40.520
bunch of support requests or whatever you call them, issues.

00:23:40.520 --> 00:23:46.260
So pytestCheck, I went in last night, I went and cleaned all those up and solved a couple

00:23:46.260 --> 00:23:47.120
of minor problems.

00:23:47.640 --> 00:23:52.600
But one of the things that I ran into that was interesting and I don't, I mean, I just

00:23:52.600 --> 00:23:55.600
kind of wanted to highlight it, is plugin for pytest.

00:23:55.600 --> 00:23:57.920
There are other plugins for pytest.

00:23:57.920 --> 00:24:03.380
Some of them don't work together very well because of all the way they abuse and use

00:24:03.380 --> 00:24:03.940
pytest.

00:24:03.940 --> 00:24:07.600
I'm definitely abusing pytest hook functions with pytestCheck.

00:24:08.380 --> 00:24:13.340
intentionally what it does is it allows you to check certain things within your test,

00:24:13.340 --> 00:24:17.380
but not fail right away so that you can continue on.

00:24:17.380 --> 00:24:23.540
And then if any of the checks fail, it actually fails the entire test and tells you all of the

00:24:23.540 --> 00:24:23.700
failures.

00:24:23.700 --> 00:24:26.440
It fails them at the end, not as it hits the first one, right?

00:24:26.720 --> 00:24:27.120
Yes.

00:24:27.120 --> 00:24:31.880
But to get away with that, the only way I could figure out is to hook into the report

00:24:31.880 --> 00:24:36.400
function, which happens much later after the test completes.

00:24:36.400 --> 00:24:41.640
Well, so there's a whole bunch of other plugins that allow you to rerun tests if they fail.

00:24:41.640 --> 00:24:46.280
There's rerun failures, there's flaky, there's retry, and there's a handful of others.

00:24:47.040 --> 00:24:52.420
Most of them are not compatible with pytestCheck because of the way, at the time that they're

00:24:52.420 --> 00:24:55.540
checking to see if something fails and the time I'm checking.

00:24:55.540 --> 00:25:00.720
So I guess I just want to point out that if you want that to happen, rerun failures works,

00:25:00.720 --> 00:25:02.200
flaky and retry don't.

00:25:02.200 --> 00:25:02.600
Nice.

00:25:02.600 --> 00:25:03.820
Oh, that's really cool.

00:25:03.820 --> 00:25:09.880
I wonder if you could like monkey patch flaky or retry to like force it to check later or

00:25:09.880 --> 00:25:10.420
something like that.

00:25:10.420 --> 00:25:10.900
Maybe.

00:25:10.900 --> 00:25:15.520
But also I actually commented in the defect report that it doesn't work with flaky.

00:25:15.940 --> 00:25:17.660
And I said, well, I think it should try.

00:25:17.660 --> 00:25:22.640
And I had a comment from somebody that said, you're just going to kill yourself off if you

00:25:22.640 --> 00:25:26.000
think that you're going to try to make it compatible with all the plugins out there.

00:25:26.000 --> 00:25:32.320
So as long as there is a workaround, it's fine to say, if you need this to work with something

00:25:32.320 --> 00:25:34.860
like this, use this other plugin and not my problem.

00:25:34.860 --> 00:25:35.460
Yeah.

00:25:35.460 --> 00:25:38.560
It seems cold, but open source is a side project.

00:25:38.560 --> 00:25:39.680
So yeah, absolutely.

00:25:39.680 --> 00:25:40.080
Cool.

00:25:40.080 --> 00:25:41.840
Well, I've got a couple of short ones here.

00:25:42.060 --> 00:25:47.840
Jeremy Schendel sent in just a quick message that Pandas is now 1.0.

00:25:47.840 --> 00:25:53.540
It had been living on the zero ver branch for a long time, but it has migrated over to semantic

00:25:53.540 --> 00:25:54.160
versioning.

00:25:54.160 --> 00:25:55.860
It has a couple of new cool features.

00:25:55.860 --> 00:25:58.160
So we're already speaking about Pandas earlier.

00:25:58.160 --> 00:26:01.280
If you're using Pandas or whatever, you know, hey, Pandas 1.0 is out.

00:26:01.280 --> 00:26:01.900
That's a big deal.

00:26:01.900 --> 00:26:04.480
Probably also means a lot for the stability of the API.

00:26:04.480 --> 00:26:05.360
Yeah, it's good.

00:26:05.440 --> 00:26:10.240
For the PyCharm fans out there, myself included, friend of the show, Anthony Shaw has created

00:26:10.240 --> 00:26:13.660
a PyCharm plugin called Python Security.

00:26:13.660 --> 00:26:14.740
So we'll link to that.

00:26:14.740 --> 00:26:19.880
And basically what it does is it goes through, much like when you're working with PyCharm,

00:26:19.880 --> 00:26:23.080
it automatically tells you, oh, you're doing a type mismatch.

00:26:23.080 --> 00:26:27.860
You're passing an int and it expects a string, or you're calling this function and it takes

00:26:27.860 --> 00:26:29.200
two arguments, but you're giving it three.

00:26:29.200 --> 00:26:30.820
It does all that checking in real time.

00:26:31.440 --> 00:26:32.660
This one is for security.

00:26:32.660 --> 00:26:38.480
So it checks for unsafe loading the YAML files, remote code execution in Flask, man in the

00:26:38.480 --> 00:26:43.960
middle with requests or HTTPX, and debug configs in like Flask and Django stuff.

00:26:43.960 --> 00:26:44.800
So that's kind of cool.

00:26:44.800 --> 00:26:45.580
You want that?

00:26:45.580 --> 00:26:46.780
Get that and install it.

00:26:46.780 --> 00:26:47.120
Nice.

00:26:47.120 --> 00:26:47.420
Yeah.

00:26:47.420 --> 00:26:52.880
And then finally, I have my Python for Decision Makers course that sort of talks about whether

00:26:52.880 --> 00:26:57.540
or not you should and how to position adopting Python at your organization.

00:26:57.540 --> 00:27:00.100
So I did a webcast on that and that's already passed.

00:27:00.100 --> 00:27:00.860
That went really well.

00:27:00.860 --> 00:27:02.880
But the recording of it is out.

00:27:02.880 --> 00:27:05.500
So I'll link to the recording if people want to sign up.

00:27:05.500 --> 00:27:08.800
You got to like register for the thing, but then you just watch the recording.

00:27:08.800 --> 00:27:10.000
Oh, I'll have to check that out.

00:27:10.000 --> 00:27:10.320
Nice.

00:27:10.320 --> 00:27:10.540
Yeah.

00:27:10.540 --> 00:27:10.860
Yeah.

00:27:10.860 --> 00:27:11.220
It was fun.

00:27:11.220 --> 00:27:11.760
A lot of fun.

00:27:11.760 --> 00:27:13.220
A lot of good conversations there.

00:27:13.220 --> 00:27:13.860
All right.

00:27:13.860 --> 00:27:16.300
I don't know about this joke, but I'm going to do it anyway.

00:27:16.300 --> 00:27:16.720
You ready?

00:27:16.720 --> 00:27:17.080
Yeah.

00:27:17.080 --> 00:27:21.640
You've heard about optimists and pessimists and a glass, right?

00:27:21.640 --> 00:27:26.600
A glass is either half full or half empty, depending on which side of that divide you land

00:27:26.600 --> 00:27:26.900
on, right?

00:27:26.900 --> 00:27:27.160
Yep.

00:27:27.160 --> 00:27:29.780
Well, there's a third angle here.

00:27:29.780 --> 00:27:34.920
And for the engineer, you don't see the glass is half full or half empty.

00:27:34.920 --> 00:27:37.340
No, the glass is twice as large as it needs to be.

00:27:37.340 --> 00:27:37.980
Exactly.

00:27:40.180 --> 00:27:41.680
It's all about capacity planning.

00:27:41.680 --> 00:27:42.140
Come on.

00:27:42.140 --> 00:27:42.700
Yeah.

00:27:42.700 --> 00:27:43.120
Okay.

00:27:43.120 --> 00:27:47.860
So I don't have a joke, but I came up with a little bit of a brain teaser this morning.

00:27:47.860 --> 00:27:48.060
Okay.

00:27:48.060 --> 00:27:48.740
Nice.

00:27:48.740 --> 00:27:49.520
Let's have it.

00:27:49.520 --> 00:27:50.200
Yeah.

00:27:50.200 --> 00:27:52.020
When is 90 greater than 100?

00:27:52.020 --> 00:27:53.900
When is 90 greater than 100?

00:27:53.900 --> 00:27:54.620
Yeah.

00:27:54.620 --> 00:27:56.940
Well, there's a couple places.

00:27:56.940 --> 00:28:01.840
One, which I was informed on Twitter, is when you're comparing a string literals.

00:28:01.840 --> 00:28:02.200
True.

00:28:02.200 --> 00:28:02.520
Yeah.

00:28:02.520 --> 00:28:02.820
Yeah.

00:28:02.820 --> 00:28:06.520
If you're going to say quote 90 less than quote 100, it's false.

00:28:06.520 --> 00:28:06.740
Yeah.

00:28:06.740 --> 00:28:06.980
Okay.

00:28:06.980 --> 00:28:08.960
The other one is microwave times.

00:28:08.960 --> 00:28:11.440
So 100.

00:28:11.440 --> 00:28:13.240
Nice.

00:28:13.240 --> 00:28:14.480
Anyway.

00:28:14.480 --> 00:28:14.960
Very good.

00:28:14.960 --> 00:28:15.700
That's it.

00:28:15.700 --> 00:28:16.100
All right.

00:28:16.100 --> 00:28:18.380
Well, you've left people with something to think about.

00:28:18.380 --> 00:28:19.660
And yeah, thanks for being here.

00:28:19.660 --> 00:28:20.480
Thanks.

00:28:20.480 --> 00:28:20.820
Bye.

00:28:20.820 --> 00:28:21.100
Yeah.

00:28:21.160 --> 00:28:21.340
Bye.

00:28:21.340 --> 00:28:23.180
Thank you for listening to Python Bytes.

00:28:23.180 --> 00:28:25.720
Follow the show on Twitter via at Python Bytes.

00:28:25.720 --> 00:28:28.560
That's Python Bytes as in B-Y-T-E-S.

00:28:28.560 --> 00:28:31.800
And get the full show notes at pythonbytes.fm.

00:28:31.800 --> 00:28:36.020
If you have a news item you want featured, just visit pythonbytes.fm and send it our way.

00:28:36.020 --> 00:28:38.700
We're always on the lookout for sharing something cool.

00:28:38.700 --> 00:28:41.840
On behalf of myself and Brian Okken, this is Michael Kennedy.

00:28:41.840 --> 00:28:45.260
Thank you for listening and sharing this podcast with your friends and colleagues.