WEBVTT

00:00:00.001 --> 00:00:04.800
Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to your earbuds.

00:00:04.800 --> 00:00:09.360
This is episode 164, recorded January 9, 2020.

00:00:09.360 --> 00:00:10.920
And I'm Brian Okken.

00:00:10.920 --> 00:00:11.820
And I'm Michael Kennedy.

00:00:11.820 --> 00:00:14.400
And this episode is brought to you by Datadog.

00:00:14.400 --> 00:00:16.100
We'll talk more about them later.

00:00:16.100 --> 00:00:19.120
But speaking of data, data-driven journalism.

00:00:19.120 --> 00:00:20.300
Yeah, let's start there.

00:00:20.300 --> 00:00:25.580
So there's this project called CJ Workbench, or just Workbench, something like this.

00:00:25.580 --> 00:00:29.600
And this recommendation comes to us from Michael Palowski.

00:00:29.600 --> 00:00:32.780
Sorry if I kind of mispronounced your name there, but thank you, Michael.

00:00:32.780 --> 00:00:42.420
And it's data-driven journalism as a service, as a platform, along with training on how to use it.

00:00:42.420 --> 00:00:46.860
Not training on journalism, but training on basically how to do data journalism.

00:00:46.860 --> 00:00:48.360
So it's a pretty cool idea.

00:00:48.360 --> 00:00:49.060
That is neat.

00:00:49.060 --> 00:00:49.340
Yeah.

00:00:49.500 --> 00:00:54.720
And so what you do is you can go and say, point it at, I don't know, some website.

00:00:54.720 --> 00:00:56.980
And say, I want you to scrape this from the website.

00:00:56.980 --> 00:00:59.240
And then you go and you fill in, like, this UI.

00:00:59.240 --> 00:01:03.340
You fill in and say, okay, now I want to filter out the empty columns.

00:01:03.340 --> 00:01:07.060
And now I want to do this and that transform and other transforms and so on.

00:01:07.060 --> 00:01:12.660
And so it basically lets you do kind of what you might do with a Jupyter notebook.

00:01:13.300 --> 00:01:16.740
But there's no code for the journalist, which is pretty cool.

00:01:16.740 --> 00:01:21.740
And so you might wonder, well, if there's no code, why are we talking about it?

00:01:21.740 --> 00:01:25.140
There's all sorts of software that does interesting stuff that we don't talk about.

00:01:25.140 --> 00:01:30.400
Well, one of the things you can do is if you find that the built-in features are not enough,

00:01:30.400 --> 00:01:35.640
then you write a few lines of Python and you start extending it for the journalist using Python,

00:01:35.640 --> 00:01:37.080
which is a pretty cool way.

00:01:37.240 --> 00:01:41.140
So instead of you worrying about the UI and all the basic stuff like loading CSVs

00:01:41.140 --> 00:01:44.080
and working with data frames and all that, you just say, use this.

00:01:44.080 --> 00:01:45.880
And if we got to write a little Python code, we will.

00:01:45.880 --> 00:01:50.580
You can use their system and just add your own little code snippets to it?

00:01:50.580 --> 00:01:51.240
Yeah, exactly.

00:01:51.240 --> 00:01:55.640
So if you look at the features, it has modules to scrape, clean, analyze, and visualize data.

00:01:55.640 --> 00:02:01.080
It's got built-in training, like it'll teach you how to use the feature that you've got built in.

00:02:01.080 --> 00:02:06.880
It connects to things like social and Google Drive and APIs, like JSON endpoints, I think.

00:02:07.420 --> 00:02:10.400
It has recorded workflows, so it's really repeatable.

00:02:10.400 --> 00:02:11.540
Versioning.

00:02:11.540 --> 00:02:18.140
But finally, you can write custom modules in Python and then add them to the little drop-down module library that you can use.

00:02:18.140 --> 00:02:18.920
Oh, that's neat.

00:02:18.920 --> 00:02:19.580
Yeah.

00:02:19.580 --> 00:02:19.880
Yeah.

00:02:19.880 --> 00:02:25.000
Plus, this is all in GitHub, so that if you really want to hack the back end, you can also.

00:02:25.000 --> 00:02:25.700
Yeah, exactly.

00:02:25.700 --> 00:02:26.740
It's open source.

00:02:26.740 --> 00:02:27.300
It's on GitHub.

00:02:27.300 --> 00:02:29.340
You can go grab it, download it, and run with it.

00:02:29.340 --> 00:02:31.240
But they also have it online as a service.

00:02:31.240 --> 00:02:33.500
So you can decide, do you want to run your own server?

00:02:33.500 --> 00:02:36.020
Do you want to use their public server that's in beta?

00:02:36.620 --> 00:02:37.120
Or whatnot.

00:02:37.120 --> 00:02:44.280
So I covered this partly because I think it's really cool, and I also covered it because, boy, I think the world needs journalism right now.

00:02:44.280 --> 00:02:44.800
Yes.

00:02:44.800 --> 00:02:48.740
And, yeah, more people saying, I don't know if I believe your analysis.

00:02:48.740 --> 00:02:50.580
I want to do my own analysis on it.

00:02:50.580 --> 00:02:52.900
Yeah, and here you don't have to commit to a huge amount of work.

00:02:52.900 --> 00:02:54.760
It's cool you pointed all these live sources.

00:02:54.760 --> 00:03:04.000
Like, you can point it at some website, and it can, like, real-time refresh the live data that it's scraping for your article so it can evolve over time and whatnot.

00:03:04.000 --> 00:03:04.580
It's pretty cool.

00:03:04.580 --> 00:03:05.580
I might have to try this.

00:03:05.580 --> 00:03:06.160
Yeah.

00:03:06.160 --> 00:03:06.680
Yeah, nice.

00:03:07.280 --> 00:03:09.360
Have we covered GUIs yet on this podcast?

00:03:09.360 --> 00:03:12.840
I think I do remember way back when at some point we talked about GUIs, right?

00:03:12.840 --> 00:03:14.660
Yeah, at least once.

00:03:14.660 --> 00:03:15.040
Yeah.

00:03:15.040 --> 00:03:15.740
So...

00:03:15.740 --> 00:03:16.600
What do you got for us this time?

00:03:16.600 --> 00:03:18.780
Well, so here's another one, another GUI.

00:03:18.780 --> 00:03:20.200
This one's called Remy.

00:03:20.200 --> 00:03:23.160
The Remy comes from Remote Interface Library.

00:03:23.160 --> 00:03:25.060
I'm not sure why it's called that.

00:03:25.060 --> 00:03:25.940
All GUIs are essentially...

00:03:25.940 --> 00:03:27.140
Why is it not Remy?

00:03:27.140 --> 00:03:28.480
Remy, I don't know.

00:03:28.480 --> 00:03:29.660
No, no, it's cool.

00:03:29.660 --> 00:03:34.320
But Remy, a platform-independent Python GUI library for your applications.

00:03:34.320 --> 00:03:43.680
And yes, we've covered things like this a lot of, like, okay, I want to just scrap the idea of a native user interface, and we'll just use web user interfaces.

00:03:43.680 --> 00:03:47.220
The thing that I liked about this, it's so small.

00:03:47.220 --> 00:03:48.440
There's no dependencies.

00:03:48.440 --> 00:03:55.920
I'm not sure why they're pointing you to do a pip install from their GitHub repo, but I'm guessing they're not really that great about...

00:03:55.920 --> 00:03:58.920
updating the PyPI version, because it's there also.

00:03:58.920 --> 00:04:01.760
But anyway, I did this morning.

00:04:01.760 --> 00:04:03.320
Grabbed a...

00:04:03.320 --> 00:04:06.880
Just did a pip install of Remy, but from their Git repo.

00:04:06.880 --> 00:04:11.740
And then I tried their little sample basic app, and I'm like, is it really this easy?

00:04:11.740 --> 00:04:16.720
And yeah, this is like 30 lines of code or less to just write.

00:04:16.720 --> 00:04:19.120
And if you run it, just Python bloop.

00:04:19.120 --> 00:04:23.280
Oh, first of all, when you pip install it, it installs the package, and then that's it.

00:04:23.280 --> 00:04:24.700
It doesn't install anything else.

00:04:25.080 --> 00:04:28.040
Because it's only dependent on the standard library, which is cool.

00:04:28.040 --> 00:04:36.860
And then you start it up, and it runs a little server on your thing, and it's really easy to throw buttons together and stuff.

00:04:36.860 --> 00:04:46.480
It looks pretty good enough, and the code is simple enough that I thought I'd include it, because there's a lot of cases where, like in an internal team, where you have...

00:04:47.020 --> 00:04:57.100
If you want to throw something together where people have access to some Python data or some Python analysis with a user interface, and something like this would be cool.

00:04:57.100 --> 00:04:58.200
And it's neat.

00:04:58.200 --> 00:04:59.100
Yeah, it's pretty neat.

00:04:59.200 --> 00:05:07.680
It's like a Python-only electron.js-ish type of thing without the HTML part.

00:05:07.680 --> 00:05:14.300
So you define your app, and you programmatically define, like, I want a button here, and I want a label there.

00:05:14.300 --> 00:05:17.480
And when I click the button, I want it to call this other Python function.

00:05:17.660 --> 00:05:28.140
So you just write pure Python, and then it turns the GUI definition into HTML, shows that in a server, and then calls back to your code, something like that, which is pretty cool, right?

00:05:28.140 --> 00:05:33.360
Yeah, and it feels similar to working with TK back in the day.

00:05:33.360 --> 00:05:40.100
Within the Python code, you create a label object and a button object, and then you put them in a container.

00:05:40.100 --> 00:05:49.420
There's no packing so far that I can see, but it has different types of containers that you append them, and then just throw those in a box.

00:05:49.420 --> 00:05:52.640
So, yeah, like you said, you don't have to write any of the HTML.

00:05:52.640 --> 00:05:54.340
It's just all Python code.

00:05:54.620 --> 00:05:58.360
So, yeah, for what it's worth, it's kind of simple.

00:05:58.360 --> 00:06:02.860
Yeah, it's a nice, simple little thing, and it looks pretty cool, and yeah, I kind of like it.

00:06:02.860 --> 00:06:10.060
One of the things I was thinking about doing was, sorry, was we have a switch box that's controlled through.

00:06:10.060 --> 00:06:19.260
We've got a Python thing that controls the switches, and sometimes it'd be nice to be able to just see what the current setup is and just have a web page that just shows that.

00:06:19.260 --> 00:06:22.700
It'd be really easy to write in Python, kind of hard to do.

00:06:22.960 --> 00:06:31.060
I don't want to deal with a whole user interface sort of thing, but having that, I think I could probably throw that together with this tool in like half an hour.

00:06:31.060 --> 00:06:38.380
Yeah, and it's a proper local application, so it can look around on the system and do what it needs to do.

00:06:38.380 --> 00:06:40.260
It needs to be really a web server.

00:06:40.260 --> 00:06:40.580
Yeah.

00:06:40.580 --> 00:06:46.960
Although you can expose, I saw there's a way to tell it to listen on the network, broader interface, not just local host.

00:06:47.040 --> 00:06:51.160
So you can't expose this thing to sort of the world, for what that's worth.

00:06:51.160 --> 00:06:52.740
I don't know if it's a good idea or not, but you could.

00:06:52.740 --> 00:06:58.720
Like I said, we would do it within our internal network, so there's no malicious code flying around internally.

00:06:58.720 --> 00:06:59.800
Yeah, yeah.

00:06:59.800 --> 00:07:00.900
Yeah, cool.

00:07:00.900 --> 00:07:01.180
Anyway.

00:07:01.180 --> 00:07:09.920
You know, I'm starting to get excited again about Qt after Ogie Moore's presentation at the last PDX Python West meetup.

00:07:09.920 --> 00:07:11.680
Like that looked pretty compelling, actually.

00:07:11.680 --> 00:07:12.100
Yeah.

00:07:12.100 --> 00:07:14.000
What a slick program that was.

00:07:14.000 --> 00:07:14.700
And zippy.

00:07:14.700 --> 00:07:16.080
Very zippy.

00:07:16.080 --> 00:07:16.980
Nice.

00:07:16.980 --> 00:07:18.480
Speaking of slick, Datadog.

00:07:18.740 --> 00:07:19.060
Yeah.

00:07:19.060 --> 00:07:31.320
Python Bytes today is sponsored by Datadog, a cloud-scale monitoring analytics platform that unifies metrics, logs, and distributed traces from your Python application.

00:07:31.320 --> 00:07:43.920
The Datadog tracing client auto instruments popular frameworks and libraries such as Django, Flask, Postgres, and AsyncIO even, so you can quickly get deep visibility into your application.

00:07:44.620 --> 00:07:48.080
Trace requests across services boundaries with flame graphs.

00:07:48.080 --> 00:07:49.260
I like flame graphs.

00:07:49.260 --> 00:07:55.200
Correlate traces with logs and metrics and plot your application architecture with the service map.

00:07:55.200 --> 00:08:02.420
Sign up for free for a free trial at pythonbytes.fm/Datadog and you'll receive a free t-shirt.

00:08:02.420 --> 00:08:02.720
Nice.

00:08:02.720 --> 00:08:04.060
Yeah, they're doing good stuff.

00:08:04.060 --> 00:08:04.760
Thanks, Datadog.

00:08:04.760 --> 00:08:08.280
This next one, Brian, you wanted it, but I got it.

00:08:08.280 --> 00:08:09.400
Yeah, you got it first.

00:08:09.400 --> 00:08:10.080
It's cool.

00:08:10.080 --> 00:08:11.040
I'm excited about it.

00:08:11.160 --> 00:08:16.560
Yeah, so there's a couple of cool libraries for building CLIs.

00:08:16.560 --> 00:08:19.680
Click is probably the most prevalent one.

00:08:19.680 --> 00:08:21.480
We've got argpars.

00:08:21.480 --> 00:08:32.540
But people who do FastAPI, the cool new API framework, these folks have now gone, let me just double check that it's actually the same organization.

00:08:32.540 --> 00:08:32.920
Yep.

00:08:33.020 --> 00:08:33.520
Yes, good.

00:08:33.520 --> 00:08:34.480
Yeah, same work.

00:08:34.480 --> 00:08:34.820
Perfect.

00:08:34.820 --> 00:08:34.860
Perfect.

00:08:34.860 --> 00:08:40.220
So they've gone and created a CLI definition library like Click.

00:08:40.920 --> 00:08:43.140
And I believe it's actually based on Click.

00:08:43.140 --> 00:08:44.280
Yeah, it's based on Click.

00:08:44.280 --> 00:08:47.200
So you might wonder, well, what does it do?

00:08:47.200 --> 00:08:49.320
The name gives you a little bit of a hint.

00:08:49.320 --> 00:08:51.000
It's based on Python type hints.

00:08:51.000 --> 00:08:53.660
So Typer looks at the various hints.

00:08:53.660 --> 00:08:56.140
It can, you can say like, I have a function.

00:08:56.140 --> 00:08:58.820
It takes a name, which is, you know, name colon stir.

00:08:58.820 --> 00:08:59.680
It takes a string.

00:08:59.880 --> 00:09:01.060
And then you just use it.

00:09:01.060 --> 00:09:09.200
And when you go run it, it'll say, hey, this thing that we ran, in order to call it, you have to specify a name and the name has to be a string and so on.

00:09:09.200 --> 00:09:15.700
So it just looks at the arguments and the types of the methods you're trying to run, in the simple case at least, and it uses that.

00:09:15.700 --> 00:09:16.540
That's pretty cool, huh?

00:09:16.540 --> 00:09:17.340
It's super cool.

00:09:17.340 --> 00:09:17.700
Yeah.

00:09:17.700 --> 00:09:20.040
And then you can grow in more complexity.

00:09:20.040 --> 00:09:21.640
Like you can have sub commands.

00:09:21.640 --> 00:09:24.480
You can have help on all the commands.

00:09:24.480 --> 00:09:26.040
You can actually get autocomplete.

00:09:26.380 --> 00:09:30.340
You can install autocomplete in your shell, all kinds of cool stuff.

00:09:30.340 --> 00:09:31.580
So you can create a little app.

00:09:31.580 --> 00:09:33.220
You say, here's a command and here's a command.

00:09:33.220 --> 00:09:35.980
And then, of course, the commands are functions.

00:09:35.980 --> 00:09:37.360
Those functions take arguments.

00:09:37.360 --> 00:09:38.400
The arguments have types.

00:09:38.400 --> 00:09:40.740
They can have default values, all that kind of stuff.

00:09:40.740 --> 00:09:42.240
So you can try to run it.

00:09:42.240 --> 00:09:47.620
It'll give you help pretty much automatically, which, yeah, this really speaks to me the way this works.

00:09:47.620 --> 00:09:49.080
I could definitely see myself using it.

00:09:49.080 --> 00:09:49.260
Yeah.

00:09:49.260 --> 00:09:55.820
So a really good, clean command line interface that has, if you're writing a tool with a command line interface,

00:09:56.000 --> 00:09:58.320
you're going to want help associated with it.

00:09:58.320 --> 00:10:02.940
You're going to want description of the different methods and what they do.

00:10:02.940 --> 00:10:07.880
But also, with as little boilerplate as possible, you don't want to clutter up your code.

00:10:07.880 --> 00:10:11.560
And as far as I'm concerned, right now, typer wins.

00:10:11.560 --> 00:10:17.280
There's hardly any extra overhead that you have to add to make this work other than type hits.

00:10:17.280 --> 00:10:23.460
And if you're not using a main or something, you have to put a little decorator on the different entry points in your program.

00:10:23.460 --> 00:10:25.200
But really clean.

00:10:25.200 --> 00:10:26.880
I'm totally going to start using this.

00:10:26.880 --> 00:10:27.680
It's really nice.

00:10:27.680 --> 00:10:32.880
And because it can leverage so much of the natural program itself, right?

00:10:32.940 --> 00:10:36.340
The argument names, the fact that it has arguments, the argument types.

00:10:36.340 --> 00:10:38.060
That's really helpful for it, yeah.

00:10:38.060 --> 00:10:47.680
So you can also integrate things like Colorama and click, which is based on, will automatically start using color for its output.

00:10:47.680 --> 00:10:50.700
If you just have Colorama around, you can also use click completion.

00:10:50.700 --> 00:10:56.400
And typer, this is the part I was talking about, will automatically configure it to provide completion for all the shells.

00:10:56.540 --> 00:10:57.920
That's really cool, too.

00:10:57.920 --> 00:11:02.980
So the one thing I was concerned about is it's based on click.

00:11:02.980 --> 00:11:06.800
And one of the things I love about click is that it has a good testing interface.

00:11:06.800 --> 00:11:11.200
You can write tests against your command line app as if you're writing from the command line,

00:11:11.200 --> 00:11:15.280
but you don't actually have to spawn a new subpros to test things.

00:11:15.280 --> 00:11:23.420
And yes, typer has the same sort of model where you can write your tests against the interface and not have to do a subprocess.

00:11:23.420 --> 00:11:24.460
Oh, that's really cool.

00:11:24.460 --> 00:11:25.300
Yeah, nice.

00:11:25.460 --> 00:11:29.760
Because, yeah, you don't have to start it basically shell out and see what happens, right?

00:11:29.760 --> 00:11:33.460
Or like issue the command line directly to the shell to see what happens.

00:11:33.460 --> 00:11:34.380
Yeah, really good job.

00:11:34.380 --> 00:11:39.700
Yeah, the other thing I like about this is you could say, well, we don't want the API of click.

00:11:39.700 --> 00:11:44.700
So we're going to start from zero and rebuild our version of what this whole world looks like.

00:11:44.700 --> 00:11:50.060
But what they did is said that we're going to build a new interface for writing against this, but on top of click.

00:11:50.060 --> 00:11:54.360
So things like Colorama and the completion just plug in seamlessly.

00:11:54.560 --> 00:11:55.520
I think that's a cool lesson.

00:11:55.520 --> 00:11:56.120
Yeah, definitely.

00:11:56.120 --> 00:11:59.080
Also, it's a smaller project for them to have to maintain, too.

00:11:59.080 --> 00:11:59.740
Yeah, exactly.

00:11:59.740 --> 00:11:59.980
Yeah.

00:11:59.980 --> 00:12:00.380
Exactly.

00:12:00.380 --> 00:12:01.180
Cool.

00:12:01.180 --> 00:12:01.460
All right.

00:12:01.460 --> 00:12:04.460
This next one that you got here is from Chris Moffitt, right?

00:12:04.460 --> 00:12:04.920
Yes.

00:12:04.920 --> 00:12:05.560
And this is actually...

00:12:06.120 --> 00:12:08.820
So I'm hoping this is completely still accurate.

00:12:08.820 --> 00:12:10.380
I imagine it is.

00:12:10.380 --> 00:12:14.620
The interface for Matplotlib hasn't changed too awfully much.

00:12:14.620 --> 00:12:20.260
I know they've had some cool releases, but I want to talk about an article called Effectively Using Matplotlib.

00:12:20.780 --> 00:12:22.740
And this is a 2017 article.

00:12:22.740 --> 00:12:28.960
But in the middle of the introduction, he says, at first, I think I was a little premature in dismissing Matplotlib.

00:12:28.960 --> 00:12:33.640
To be honest, I did not quite understand it and how to use it effectively in my workflow.

00:12:33.860 --> 00:12:37.640
And I think that matches my understanding of Matplotlib as well.

00:12:37.640 --> 00:12:43.080
I mean, when I first needed to put charts or graphs or something, of course, you try Matplotlib.

00:12:43.080 --> 00:12:43.820
And then you're...

00:12:43.820 --> 00:12:47.180
You kind of hit in the face with the complexity of the interface.

00:12:47.180 --> 00:12:56.620
And you know you can do the simple thing simple, but how to do something a little bit more less than simple or more than, you know, more complex is a little daunting.

00:12:56.620 --> 00:12:58.960
And then I went out and looked for other alternatives.

00:12:58.960 --> 00:13:02.260
But now I think the alternatives have their issues too.

00:13:02.260 --> 00:13:05.900
So I'm ready to go back and take a serious look at Matplotlib.

00:13:05.900 --> 00:13:13.380
This article is a really good look at kind of understanding it and at least Chris's workflow, how to use it.

00:13:13.560 --> 00:13:22.500
And one of the things I like is the description that one of the reasons why Matplotlib's interface might be a little confusing is because it's coming from...

00:13:22.500 --> 00:13:23.880
It's really got two interfaces.

00:13:23.880 --> 00:13:27.080
It does a Matlab-like state-based interface.

00:13:27.080 --> 00:13:33.580
And I'm not a Matlab user, but I know that a lot of people have come from Matlab to Matplotlib.

00:13:33.580 --> 00:13:39.100
And so that sort of interface is important to have that transition be easy for a lot of people.

00:13:39.100 --> 00:13:43.300
But then there's also an object-based interface that makes a lot more sense.

00:13:43.300 --> 00:13:49.060
And that's Chris's recommendation is to use the object-based interface instead.

00:13:49.060 --> 00:13:55.740
And so now after he's gone through it a few times and he has some recommendations.

00:13:55.740 --> 00:14:06.700
And his recommendations are to first learn the basic Matplotlib terminology, specifically what is figure and axes and around those.

00:14:06.700 --> 00:14:12.440
And what's difficult for me is that those are just normal words that I think I already understand them.

00:14:12.440 --> 00:14:16.720
But I need to make sure I understand what Matplotlib thinks those things are.

00:14:16.860 --> 00:14:21.480
Yeah, sometimes it's just hard to work with Matplotlib unless you find an example, right?

00:14:21.480 --> 00:14:22.620
A lot of times what I...

00:14:23.620 --> 00:14:26.700
My goal is to hunt the internet for something that looks like I want.

00:14:26.700 --> 00:14:29.180
Figure out if I can find the Python code that created it.

00:14:29.180 --> 00:14:30.340
And then I'm good.

00:14:30.340 --> 00:14:30.660
Yeah.

00:14:30.660 --> 00:14:35.000
He does have an example in this, which is kind of walks through at least one example.

00:14:35.000 --> 00:14:40.380
But if you really want to kind of get your head around it to start with using pandas with Matplotlib.

00:14:41.380 --> 00:14:49.040
So yeah, you start your visualization with basic pandas plotting and then add, if you want it more complex, try Seaborn.

00:14:49.040 --> 00:14:57.140
And then if you want to customize it a little bit more, he said use Matplotlib native stuff to customize the visualization.

00:14:57.140 --> 00:14:59.960
And I wouldn't have thought to use that workflow.

00:14:59.960 --> 00:15:01.420
So that's kind of nice.

00:15:01.420 --> 00:15:09.060
It also includes this really kind of cool handy reference that there's a little graphic that he created that has like all the different...

00:15:09.060 --> 00:15:16.200
What all the axes are and if you want to customize a certain part of a graph, what that name is within Matplotlib so you know how to look it up.

00:15:16.200 --> 00:15:17.740
And that's often...

00:15:17.740 --> 00:15:21.560
The secret of programming anymore is to know what specific word to Google.

00:15:21.560 --> 00:15:23.480
So I think that's a good thing.

00:15:23.480 --> 00:15:23.900
Nice.

00:15:23.900 --> 00:15:29.780
And also I want to remind folks, long-time listeners of PyLustrator.

00:15:29.780 --> 00:15:35.280
I'm not really sure about the pronunciation like Illustrator, but Py for the first two words.

00:15:35.280 --> 00:15:36.780
I'll put a link in the show notes.

00:15:36.780 --> 00:15:40.380
And what it is, is it lets you take a basic Matplotlib plot.

00:15:40.380 --> 00:15:45.980
It launches a GUI where you customize the look and feel and then it'll output the code that would have done that for you.

00:15:45.980 --> 00:15:46.420
Oh, cool.

00:15:46.420 --> 00:15:46.680
Yeah.

00:15:46.680 --> 00:15:50.240
We talked about that in episode 137, which is like a year ago or something.

00:15:50.240 --> 00:15:51.820
But anyway, still good stuff.

00:15:51.820 --> 00:15:52.900
So I'll put a link in there for that.

00:15:52.900 --> 00:15:56.680
I'm also including a link in a almost related section.

00:15:57.120 --> 00:15:59.640
Maybe there's an article about this, but I haven't found it.

00:15:59.640 --> 00:16:03.000
There's a Stack Overflow answer that has a little...

00:16:03.000 --> 00:16:06.020
The code you need to stick a map...

00:16:06.020 --> 00:16:14.000
Generate in a Matplotlib graph and put it in a Flask file on the fly without having to save it to a file.

00:16:14.000 --> 00:16:14.400
Oh, yeah.

00:16:14.400 --> 00:16:15.880
Like a live dashboard type thing.

00:16:15.880 --> 00:16:16.260
That's cool.

00:16:16.260 --> 00:16:16.520
Yeah.

00:16:16.520 --> 00:16:16.840
Nice.

00:16:16.940 --> 00:16:23.700
Speaking about doing stuff on the web, if you're using Django, there's a cool little library that'll let you do stuff in the background.

00:16:23.700 --> 00:16:36.460
Maybe you want to do some query and update some kind of report or you want to send out a bunch of emails or recompute some indexes or recompute some search results or something like that.

00:16:36.460 --> 00:16:42.040
And it's going to take a little while and you don't want to just have it block on your main request.

00:16:42.160 --> 00:16:43.960
There's a bunch of different things you could do.

00:16:43.960 --> 00:16:49.060
You could set up a separate server with like Celery or RabbitMQ or something like that.

00:16:49.060 --> 00:16:52.180
But that's a whole other set of servers and it just gets way more complicated.

00:16:52.180 --> 00:16:56.660
Sometimes you want to just run it in the background just in the same process, right?

00:16:56.660 --> 00:16:59.120
It's easy to share data because you just pass the pointers.

00:16:59.120 --> 00:17:02.060
So there's this thing called Django Simple Task.

00:17:02.060 --> 00:17:09.080
And it allows you to run background tasks in Django 3 without requiring any services or workers.

00:17:09.080 --> 00:17:09.540
Oh, cool.

00:17:09.540 --> 00:17:14.080
Yeah, basically the reason it has to be Django 3 is it's based on AsyncIO.

00:17:14.080 --> 00:17:18.600
And Django 3 is the first version that uses ASGI.

00:17:18.600 --> 00:17:24.280
And so you actually need to run it in ASGI server like uvicorn or something like that.

00:17:24.280 --> 00:17:28.440
So be really aware that you have to run it in its async way for this to work.

00:17:28.440 --> 00:17:34.680
But basically when it starts, when the web app starts, there's a queue created and some workers to listen to the queue.

00:17:35.060 --> 00:17:41.540
And then you just go to any function you want to run and you just call defer on it and that'll kick it off in the background.

00:17:41.540 --> 00:17:43.120
And you could do that now.

00:17:43.120 --> 00:17:46.200
Like you could create a thread and just launch it as a background thread.

00:17:46.620 --> 00:17:49.280
But one, you don't get like a thread pooling.

00:17:49.280 --> 00:18:01.500
But the other is when it's time for the worker process to shut down, like you're doing a new deploy or something, it'll actually wait until its background work gets done before it allows you to restart it, at least in a friendly way.

00:18:01.500 --> 00:18:03.060
So that's cool.

00:18:03.160 --> 00:18:12.600
And you can either give it a asyncio coroutine or just a function and it delegates it, either runs it or delegates it to a thread pool if it's not async, things like that.

00:18:12.600 --> 00:18:15.740
So it's pretty cool and it's really easy to use, like ridiculously easy.

00:18:15.740 --> 00:18:16.580
Oh, that's neat.

00:18:16.580 --> 00:18:16.820
Yeah.

00:18:17.060 --> 00:18:23.460
So there's a simple little example I put in the show notes where it just imports the deferred operation method.

00:18:23.460 --> 00:18:28.240
It has two tasks, one of them which uses time.sleep that requires threading.

00:18:28.400 --> 00:18:31.800
And one uses asyncio because it's an async method.

00:18:31.800 --> 00:18:33.440
So it would run on the async event loop.

00:18:33.440 --> 00:18:39.020
And in the view, you can just say defer one, defer method one, defer method two, and then do your regular work.

00:18:39.020 --> 00:18:41.480
And that kicks it off but doesn't wait for the response.

00:18:41.480 --> 00:18:50.520
But does it in a slightly more durable, better way than just like launching threads or kicking it off on its own little background thing to go run unmonitored.

00:18:50.520 --> 00:19:01.860
So in this example that nobody can see, but they will if they look at the show notes, will the HTTP response happen and the view return before the defer tasks get run?

00:19:01.860 --> 00:19:08.220
They might get started, but because in both of them they sleep for a second to kind of, that's their work, right?

00:19:08.220 --> 00:19:09.200
They're sleeping for a second.

00:19:09.200 --> 00:19:14.880
So certainly the response would be sent back to the user before those are done.

00:19:14.880 --> 00:19:19.180
And this is a cool interface because the two tasks, one of them is async and one is not.

00:19:19.180 --> 00:19:22.180
And the defer call doesn't, you can't tell.

00:19:22.180 --> 00:19:22.440
Right.

00:19:22.440 --> 00:19:23.560
You don't have to treat it differently.

00:19:23.560 --> 00:19:25.100
But if you can do async, great.

00:19:25.100 --> 00:19:27.700
It'll do that on the async event loop, I think.

00:19:27.700 --> 00:19:32.440
Otherwise, it just puts it into a thread pool, which is pretty cool that you don't have to worry.

00:19:32.440 --> 00:19:34.420
You can just kind of go, here's some background stuff.

00:19:34.420 --> 00:19:37.060
If it's IO driven, we'll do it this way.

00:19:37.060 --> 00:19:38.960
Otherwise, just do it that way, which is, I like it.

00:19:38.960 --> 00:19:39.260
It's nice.

00:19:39.260 --> 00:19:40.340
Yeah, really nice.

00:19:40.340 --> 00:19:40.920
Cool.

00:19:40.920 --> 00:19:41.160
Cool.

00:19:41.160 --> 00:19:41.540
All right.

00:19:41.540 --> 00:19:42.960
What's this last one you got for us?

00:19:43.820 --> 00:19:45.800
Well, so something happened recently.

00:19:45.800 --> 00:19:52.120
I've got a handful of, I don't open source, work on open source projects really as much

00:19:52.120 --> 00:19:52.760
as I'd like to.

00:19:52.760 --> 00:19:56.080
I'd like to work, do some more work on open source projects.

00:19:56.080 --> 00:19:59.620
But I have a few that I maintain myself.

00:19:59.620 --> 00:20:04.860
But project is in air quotes I'm doing because they're really pretty small projects.

00:20:04.860 --> 00:20:11.980
And a couple of them, cards and submark are things that I just created to help with teaching

00:20:11.980 --> 00:20:13.360
people how testing works.

00:20:13.760 --> 00:20:16.180
They're not really intended to be useful projects.

00:20:16.180 --> 00:20:19.980
And then another one was a plugin for pytest.

00:20:19.980 --> 00:20:23.700
Actually, I do use it and it's useful for me.

00:20:23.700 --> 00:20:29.360
But I started getting, like in the last handful of months, I started getting pull requests and

00:20:29.360 --> 00:20:30.460
issues and stuff.

00:20:30.460 --> 00:20:33.180
And I didn't know anybody was using it at all.

00:20:33.180 --> 00:20:34.440
So this was surprising to me.

00:20:34.440 --> 00:20:38.300
But I wanted to highlight, I don't know if, I can't believe we haven't covered this or maybe

00:20:38.300 --> 00:20:38.920
we already have.

00:20:39.360 --> 00:20:42.900
There's a website called PyPI stats.org.

00:20:42.900 --> 00:20:43.820
Yeah, that's news to me.

00:20:43.820 --> 00:20:44.400
I hadn't heard of it.

00:20:44.400 --> 00:20:46.220
It's just a simple interface.

00:20:46.220 --> 00:20:48.380
Now, the backend might be complicated.

00:20:48.820 --> 00:20:55.000
But in the interface, it just has like a little box and you plug in a Python package and it

00:20:55.000 --> 00:21:00.000
tells you what the download stats, actually quite a bit of history too, of different packages.

00:21:00.000 --> 00:21:04.120
And so I use that for all of my different projects.

00:21:04.380 --> 00:21:09.420
And sure enough, cards was downloaded like, I don't know, less than 400 times in the last

00:21:09.420 --> 00:21:13.160
month, submark less than 70 times in the last month.

00:21:13.160 --> 00:21:20.280
And then pytest-check, which is a little app or a plugin that lets you have multiple failures

00:21:20.280 --> 00:21:22.540
per test, but keep going during the test.

00:21:22.740 --> 00:21:28.140
That was downloaded just a little less than 20,000 times in the last month.

00:21:28.140 --> 00:21:29.960
So clearly that one's more.

00:21:29.960 --> 00:21:34.700
And that's the reason why I'm having more people try and tell me what's wrong with it.

00:21:34.700 --> 00:21:36.780
So I guess I have to start maintaining.

00:21:36.780 --> 00:21:39.020
Yeah, this is a super cool interface.

00:21:39.020 --> 00:21:43.640
You get all kinds of graphs and ability to slice it by dates and whatnot.

00:21:43.640 --> 00:21:45.240
And yeah, I like it.

00:21:45.240 --> 00:21:46.300
And there's also a top 20.

00:21:46.300 --> 00:21:50.420
So you can look at like really what are the top 20 packages on PyPI.

00:21:50.780 --> 00:21:57.380
And that's why I say that mine are small peanuts because at the most, it's like 19,000 a month.

00:21:57.380 --> 00:22:02.340
But the top 20 all get over 1.3 million downloads per day.

00:22:02.340 --> 00:22:02.940
Wow.

00:22:02.940 --> 00:22:03.480
Yeah.

00:22:03.480 --> 00:22:05.160
And you can look at them, look at the trends.

00:22:05.160 --> 00:22:05.900
That's pretty interesting.

00:22:05.900 --> 00:22:08.560
There's probably some cool stuff to go through here.

00:22:08.560 --> 00:22:14.220
And I guess if you were trying to do predictive stuff or analytics, there's probably a lot of

00:22:14.220 --> 00:22:14.840
things to be done here.

00:22:14.840 --> 00:22:15.620
Yeah, definitely.

00:22:15.620 --> 00:22:18.980
Also, just occasionally, I'm just the top 20.

00:22:18.980 --> 00:22:24.060
I like because I was occasionally I'm curious about that of like, what are the top packages

00:22:24.060 --> 00:22:24.700
on PyPI?

00:22:24.700 --> 00:22:25.940
What do people use it?

00:22:25.940 --> 00:22:26.080
Yeah.

00:22:26.080 --> 00:22:28.160
They weren't what I expected.

00:22:28.160 --> 00:22:33.280
I mean, some of the ones I obviously expected were, but I think right now URLlib was the top

00:22:33.280 --> 00:22:33.520
top.

00:22:33.520 --> 00:22:35.080
And I didn't expect that.

00:22:35.080 --> 00:22:36.120
So yeah.

00:22:36.120 --> 00:22:36.280
Yeah.

00:22:36.280 --> 00:22:37.780
URLlib 3 specifically.

00:22:37.780 --> 00:22:38.740
But yeah.

00:22:38.840 --> 00:22:40.680
It must be used by a whole bunch of other stuff.

00:22:40.680 --> 00:22:41.640
Like 6.

00:22:41.640 --> 00:22:44.160
I don't think people are specifically downloading 6.

00:22:44.160 --> 00:22:45.980
It's the 2 or 3.

00:22:46.060 --> 00:22:51.000
Like I think Request uses both of those and Certify and that like hits the top 4.

00:22:51.000 --> 00:22:53.240
So Python.

00:22:53.240 --> 00:22:54.700
DateUtil is a good one up there.

00:22:54.700 --> 00:22:55.520
Boto Core.

00:22:55.520 --> 00:22:56.360
Pretty interesting.

00:22:56.360 --> 00:23:03.540
I use Boto 3, which depends on Boto 4, but it kind of makes me frustrated because Boto 3 won't

00:23:03.540 --> 00:23:07.340
use the latest version of Boto Core, but it's all made by the same people.

00:23:07.340 --> 00:23:10.120
Anyway, there's a bunch of interesting stuff up here.

00:23:10.120 --> 00:23:10.300
Yeah.

00:23:10.300 --> 00:23:12.160
I think I'm going to have to look at this some more.

00:23:12.160 --> 00:23:13.440
Pip is in the top 20.

00:23:13.440 --> 00:23:15.020
Nice.

00:23:15.020 --> 00:23:16.360
Got to get started somewhere.

00:23:16.360 --> 00:23:17.480
Yeah.

00:23:17.480 --> 00:23:18.080
All right.

00:23:18.080 --> 00:23:19.600
Well, that's it for all of our main items.

00:23:19.600 --> 00:23:20.620
What you got for the extras?

00:23:20.620 --> 00:23:22.980
Well, I wanted to actually just ask for some help.

00:23:22.980 --> 00:23:30.000
The last episode we put out was a live recording of the January Python PDX West meetup.

00:23:30.000 --> 00:23:32.660
Actually, not the entire meetup, but just a-

00:23:32.660 --> 00:23:34.840
Flash in case people haven't been paying attention.

00:23:34.840 --> 00:23:36.080
They're near Portland.

00:23:36.080 --> 00:23:39.620
There's a second Python meetup, which runs out in Hillsborough.

00:23:39.620 --> 00:23:41.780
Much better for Westside folks.

00:23:41.780 --> 00:23:43.120
So yeah, you put that together.

00:23:43.120 --> 00:23:43.480
Good job.

00:23:43.480 --> 00:23:43.800
Yeah.

00:23:43.800 --> 00:23:47.720
And one of the things that we wanted to do with this one was to, I got this from a lot

00:23:47.720 --> 00:23:48.100
of people.

00:23:48.100 --> 00:23:53.640
And in the tech community, well, the Westside has a lot of high-tech stuff.

00:23:53.640 --> 00:23:56.400
There's a lot of people coming from other languages to Python.

00:23:56.400 --> 00:24:00.880
And so they might not be beginner programmers, but they're beginners at Python.

00:24:01.380 --> 00:24:08.060
We've had this before, people saying some beginner-friendly content per at least one topic per meetup so

00:24:08.060 --> 00:24:09.600
that people aren't lost with everything.

00:24:09.600 --> 00:24:14.000
And yeah, we kind of forgot this last time, but I enjoyed the topics.

00:24:14.000 --> 00:24:19.080
I'd like to hear from other people as to what sort of topics would be good at a meetup for

00:24:19.080 --> 00:24:20.360
Python beginner-friendly.

00:24:20.360 --> 00:24:21.840
And just let us know.

00:24:21.840 --> 00:24:22.620
I think that'd be cool.

00:24:22.720 --> 00:24:22.840
Yeah.

00:24:22.840 --> 00:24:28.280
Well, it's easy to get super into focusing on what's the most latest, coolest, hottest

00:24:28.280 --> 00:24:29.020
thing.

00:24:29.020 --> 00:24:35.100
But there's a lot of folks who really just appreciate setting the ground, a good foundation.

00:24:35.100 --> 00:24:36.940
And so, yeah, I think that's a good topic.

00:24:36.940 --> 00:24:37.180
Yeah.

00:24:37.180 --> 00:24:41.420
And one of the things I'd like to do is possibly get a list of those sorts of things so that

00:24:41.420 --> 00:24:44.540
we can try to ask people to put together something.

00:24:45.000 --> 00:24:52.340
Like one of the meetups I did, like just describing how pip and virtual environment work and how

00:24:52.340 --> 00:24:52.900
to use those.

00:24:52.900 --> 00:24:57.700
And there's a lot of basic stuff that people don't even think about maybe that they should

00:24:57.700 --> 00:24:58.180
talk about.

00:24:58.180 --> 00:25:00.100
And it'd be good to hear more.

00:25:00.260 --> 00:25:03.320
I also want to try to get other people to do it because you and I could probably do

00:25:03.320 --> 00:25:05.100
those sorts of beginner-friendly ones.

00:25:05.100 --> 00:25:05.380
Sure.

00:25:05.380 --> 00:25:07.240
It'd be fun to have other speakers too.

00:25:07.240 --> 00:25:07.860
Yeah, absolutely.

00:25:07.860 --> 00:25:09.260
It would be very fun.

00:25:09.260 --> 00:25:14.620
If people are willing to get to Portland on a Tuesday evening, then they can come give a

00:25:14.620 --> 00:25:14.980
presentation.

00:25:14.980 --> 00:25:15.500
Yeah.

00:25:15.500 --> 00:25:16.540
One more thing.

00:25:16.540 --> 00:25:19.120
I want to do a shout out to PyCascades 2020.

00:25:19.120 --> 00:25:20.400
I can't remember when.

00:25:20.400 --> 00:25:21.700
It's in February coming up.

00:25:21.700 --> 00:25:22.920
February, I believe.

00:25:22.920 --> 00:25:25.080
I believe it's like 8th and 9th.

00:25:25.080 --> 00:25:25.800
Let me double check.

00:25:25.800 --> 00:25:29.120
It is February 8th and 9th in Portland, Eastside.

00:25:29.120 --> 00:25:29.740
Wonderful.

00:25:29.980 --> 00:25:31.740
So people should get tickets so they could come.

00:25:31.740 --> 00:25:36.460
And if you haven't bought your ticket yet, check the show notes because we've got a discount

00:25:36.460 --> 00:25:37.760
code for 10% off.

00:25:37.760 --> 00:25:38.420
Yeah, absolutely.

00:25:38.420 --> 00:25:39.460
Yeah, it's right there at the bottom.

00:25:39.460 --> 00:25:43.380
I don't really have anything this week for extras to share.

00:25:43.380 --> 00:25:47.980
But one thing that did come out and caught my attention yesterday that I think I might as

00:25:47.980 --> 00:25:54.600
well just throw out that's kind of cool is Firefox 72 is out and it has come with anti-fingerprinting,

00:25:54.600 --> 00:25:56.560
which is pretty exciting.

00:25:56.560 --> 00:25:58.320
So I don't know how many people are aware.

00:25:58.320 --> 00:25:59.260
I don't know if you're aware, Brian.

00:25:59.700 --> 00:26:04.300
That even with ad blockers and blocking like tracking cookies and stuff, there's all these

00:26:04.300 --> 00:26:07.360
weird things that you can do to figure out.

00:26:07.360 --> 00:26:09.760
Actually, it's that same person, right?

00:26:09.760 --> 00:26:15.740
Like you can ask, what are all the installed fonts and what version of OS do you have?

00:26:15.740 --> 00:26:17.380
And here's a canvas thing.

00:26:17.380 --> 00:26:22.280
Let's run some stuff on there and like try to detect weird CPU cycles and other kinds of

00:26:22.280 --> 00:26:26.680
there's all these like weird things you can do in browsers and pretty much uniquely identify

00:26:26.680 --> 00:26:28.740
everyone running a web browser.

00:26:28.920 --> 00:26:29.580
Oh, that's lame.

00:26:29.580 --> 00:26:30.780
Even without cookies.

00:26:30.780 --> 00:26:31.200
Yeah.

00:26:31.200 --> 00:26:33.680
So Firefox is trying to fight back against that.

00:26:33.680 --> 00:26:36.140
So I think that's pretty awesome and worth checking out.

00:26:36.140 --> 00:26:38.880
I'll actually link to ours Technica article about it.

00:26:38.880 --> 00:26:39.520
Okay, cool.

00:26:39.520 --> 00:26:39.800
Yep.

00:26:40.160 --> 00:26:42.600
Well, I guess that leaves us with a joke, doesn't it?

00:26:42.600 --> 00:26:43.000
Yeah.

00:26:43.140 --> 00:26:47.280
Last time we did a joke about describing different software programming jobs.

00:26:47.280 --> 00:26:52.700
This one, you're going to have to imagine that people who are fans of various programming

00:26:52.700 --> 00:26:57.680
languages are in like a literature course and they have to write an essay.

00:26:57.680 --> 00:26:57.980
Okay?

00:26:57.980 --> 00:26:58.400
Okay.

00:26:58.500 --> 00:26:58.740
Okay.

00:26:58.740 --> 00:27:00.200
Have you pulled up the picture?

00:27:00.200 --> 00:27:00.680
Yes.

00:27:00.680 --> 00:27:01.580
I'm already laughing.

00:27:01.580 --> 00:27:01.940
All right.

00:27:01.940 --> 00:27:02.140
Cool.

00:27:02.140 --> 00:27:07.700
So basically there's professor looking person with glasses trying to read like a paper, a

00:27:07.700 --> 00:27:12.580
physical paper and a student looking at it, getting the feedback from the professor.

00:27:12.580 --> 00:27:15.760
This is important because at the end it gets interesting with the pictures.

00:27:15.760 --> 00:27:19.000
So for Python, it says, this is plagiarism.

00:27:19.000 --> 00:27:20.920
You can't just import essay.

00:27:20.920 --> 00:27:28.240
For Java, it says, I'm two pages in and I have no idea what you're saying.

00:27:28.240 --> 00:27:29.240
Assembling.

00:27:29.240 --> 00:27:30.120
Assembling.

00:27:30.120 --> 00:27:30.620
Pretty appropriate.

00:27:30.620 --> 00:27:30.980
Yeah.

00:27:30.980 --> 00:27:31.560
Assembling.

00:27:31.560 --> 00:27:34.600
Do you really have to redefine every word in the English language?

00:27:34.600 --> 00:27:39.520
With C, it says, this is great, but you forgot to add a null terminator.

00:27:39.520 --> 00:27:40.680
Now I'm just reading garbage.

00:27:40.680 --> 00:27:47.160
I don't really know if I understand the C++ one, but it says, I asked for one copy, not

00:27:47.160 --> 00:27:49.300
400 and the professor's buried in books.

00:27:49.300 --> 00:27:51.340
Oh, I don't get that either, but.

00:27:51.340 --> 00:27:51.560
Yeah.

00:27:51.560 --> 00:27:52.020
Okay.

00:27:52.020 --> 00:27:52.660
Unix Shell.

00:27:52.660 --> 00:27:53.440
This one's pretty good.

00:27:53.440 --> 00:27:55.240
I don't have permission to read this.

00:27:55.920 --> 00:28:02.020
LaTeX, which you often use to describe scientific papers and super elaborate mathematical formulas,

00:28:02.020 --> 00:28:07.160
says, your paper makes no sense to me, but it's the most beautiful thing I've ever laid

00:28:07.160 --> 00:28:08.020
eyes on.

00:28:08.020 --> 00:28:11.100
And the final one is the HTML programmer.

00:28:11.100 --> 00:28:18.760
And instead of holding a paper, the professor's holding a pot and they just say, this is a flower

00:28:18.760 --> 00:28:19.080
pot.

00:28:19.080 --> 00:28:21.180
What is it?

00:28:21.380 --> 00:28:23.080
I don't get it, but it's funny.

00:28:23.080 --> 00:28:23.440
Yeah.

00:28:23.440 --> 00:28:24.540
I don't either.

00:28:24.540 --> 00:28:25.280
All right.

00:28:25.280 --> 00:28:26.820
So anyway, I thought that was pretty fun.

00:28:26.820 --> 00:28:27.960
You all can check it out.

00:28:27.960 --> 00:28:30.560
The comic is linked in there, but it's pretty fun.

00:28:30.560 --> 00:28:31.820
You can't just import essay.

00:28:31.820 --> 00:28:32.720
This is plagiarism.

00:28:32.720 --> 00:28:34.520
All right.

00:28:34.520 --> 00:28:35.780
Well, thanks a lot, Michael.

00:28:35.780 --> 00:28:36.400
Yeah, you bet.

00:28:36.400 --> 00:28:37.080
Thanks, Brian.

00:28:37.080 --> 00:28:37.720
Good to be here.

00:28:37.720 --> 00:28:39.720
Thank you for listening to Python Bytes.

00:28:39.720 --> 00:28:42.180
Follow the show on Twitter at Python Bytes.

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

00:28:45.200 --> 00:28:48.100
And get the full show notes at Pythonbytes.fm.

00:28:48.100 --> 00:28:53.160
If you have a news item you want featured, just visit Pythonbytes.fm and send it our way,

00:28:53.160 --> 00:28:55.220
where I was on the lookout for sharing something cool.

00:28:55.220 --> 00:28:59.400
This is Brian Okken, and on behalf of myself and Michael Kennedy, thank you for listening

00:28:59.400 --> 00:29:01.640
and sharing this podcast with your friends and colleagues.

