« Return to show page
Transcript for Episode #164:
Use type hints to build your next CLI app
00:00 OKKEN: Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to your earbuds. This is Episode 164 recorded January 9, 2020, and I'm Brian Okken.
00:11 KENNEDY: And I'm Michael Kennedy.
00:11 OKKEN: And this episode is brought to you by Datadog, and we'll talk more about them later. But speaking of data, data-driven journalism.
00:19 KENNEDY: Yeah, let's start there. So there's this project called CJWorkbench or just Workbench something like this, and this recommendation comes to us from Michael Paholski. Sorry if I kind of mispronounced your name there, but thank you, Michael. And it's data-driven journalism as a service, as a platform along with training on how to use it. Not training on journalism, but training on basically how to do data journalism. So it's a pretty cool idea.
00:48 OKKEN: That is neat.
00:49 KENNEDY: Yeah, and so you do is you can go and say, point it at, I don't know some website, and say I want you to scrape this from the website and then you go and you fill in in like this UI, you fill in this okay, now I want to filter out the empty columns and I want to do this and that transform, and other transforms and so on. And so it basically lets you, do kind of what you might do with a Jupyter notebook, but there's no code for the journalists, which is pretty cool. And so you might wonder, well if there's no code, why are we talking about it? There's all sorts of software that those interesting stuff that we don't talk about. Well, one of the things you can do is if you find that the built in features are not enough, then you write a few lines of Python, and you start extending it for the journalists using Python, which is a pretty cool way. So instead of you worrying about the UI and all the basic stuff like loading CSVs and working with data frames and all that, you just say this and if we got to write a little Python code, we will.
01:45 OKKEN: You can use their system and just add your own little code snippets to it?
01:50 KENNEDY: Yeah, exactly. So if you look at the features it has modules to scrape, clean, analyze and visualize data, it's got built in training like it'll teach you how to use the feature that you've got built in. It connects to things like social and Google Drive and API's like a JSON endpoints, I think. It has recorded workflow so it's really repeatable, and versioning, but finally you can write custom modules in Python and then add them to the little drop down module library that you can use.
02:18 OKKEN: Oh, that's neat, yeah. Plus this is all in GitHub so that if you really want to hack the backend, you can also.
02:25 KENNEDY: Yeah, exactly. It's open source, it's on GitHub, you can go grab it, download it and run with it, but they also have it online as a service so you can decide you want to run your own server, do you want to use their public server that's in beta or whatnot. So I covered this partly because I think it's really cool and I also covered it because well, I think the world needs journalism right now.
02:44 OKKEN: Yes. And, yeah, more people saying, "I don't know if I believe your analysis I want to do my own analysis on it."
02:50 KENNEDY: Yeah, and here you don't have to commit to a huge amount of work. It's cool you pointed all these live sources like you can point it at some website and you can like real time refresh the live data that it's scraping for your article so it can evolve over time and whatnot. Pretty cool.
03:05 OKKEN: I may have to try this. Nice.
03:07 KENNEDY: Have we covered GUIs yet on this podcast?
03:09 OKKEN: I think I do remember way back when, at some point we talked about GUIs, right?
03:13 KENNEDY: Yeah. At least once. Yeah.
03:15 OKKEN: So, what do you got for us this time?
03:16 KENNEDY: Well so here's another one, another GUI. This one's called REMI. The REMI comes from Remote Interface Library. I'm not sure why it's called that. All GUI's...
03:26 OKKEN: Why is not REMIL?
03:27 KENNEDY: REMIL, I don't know.
03:30 OKKEN: REMI a platform independent Python GUI library for your applications. 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, we'll just use web user interfaces. The thing that I liked about this, it's so small. Is there's no dependencies, you do, I'm not sure why they're pointing you to do a pip install from their GitHub repo, but I'm guessing it's they're not really that great about updating their PyPi I version because it's there also. But anyway, I did this this morning, grabbed a, just did a pip install of REMI, but from their Git repo. And then I tried their little sample basic app and I'm like, is it really this easy? And yeah this is like 30 lines of code or less. That to just write. And if you run it just Python Blip. Oh, first of all, when you pip install it, it installs the package and then that's it. It doesn't install anything else, because it's only dependent on the standard library. Which is cool. And then you start it up and it runs a lil server on your thing, and it's really easy to throw buttons together and stuff. 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 a internal team where you have a, 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. And it's neat.
04:58 KENNEDY: Yeah, it's pretty neat. It's like a Python only Electron JS-ish type of thing without the HTML part. So you define your app and you programmatically define like, I want a button here and I want a label there, and when I Click the button, I want it to call this other Python function. 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?
05:28 OKKEN: Yeah, and it's, I mean, it looks, feels a similar to working with tk back in the day of, within the Python code, you create a label object and a button object, and then you put them in a container. There's no packing that I so far that I can see, but has different types of containers that you append them and then just, you know, throw those in a box so that, yeah, right like you said, you don't have to write any of the HTML, it's just all Python code. So yeah, for what it's worth it's kind of simple.
05:58 KENNEDY: Yeah, it's a nice simple little thing and it looks pretty cool and yeah, I kind of like it.
06:02 OKKEN: One of the things I was thinking about doing was, sorry, was we have a switch box that's controlled through, 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 webpage that just shows that. It'd be really easy to write in Python. Kind of hard to do. 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 or so.
06:31 KENNEDY: Yeah, and it's, proper local application so it can, yeah, look around on the system and do what it needs to do. Doesn't need to be like, really a web server. Although you can expose, I saw there's a way to like tell it to listen on the network broader interface, not just local host. So you can expose this thing to sort of the world for what that's worth. I don't know if it's a good idea or not, but you could.
06:52 OKKEN: Like I said, we will do it within our internal network so there's no malicious code flying around internally, so.
07:00 KENNEDY: Cool.
07:01 OKKEN: Anyway.
07:02 KENNEDY: You know, I'm starting to get excited again about QT after Ogi Moore's presentation at the last PDX Python West meetup, like that looked pretty compelling actually.
07:11 OKKEN: Yeah, what a slick program that was, and zippy, so.
07:15 KENNEDY: Very zippy. Nice, speaking to slick Datadog.
07:18 OKKEN: Yeah, Python Bytes today is sponsored by Datadog, a cloud scale, monitoring, and analytics platform that unifies metrics, logs, and distributed traces from your Python application. The Datadog tracing client, auto instruments popular frameworks and libraries such as Django, Flask, Postgres and Async IO even. So you can quickly get deep visibility into your application. Trace requests across services boundaries with flame graphs. I like flame graphs. Correlate traces with logs and metrics and plot your application architecture with the service map. Sign up for free for a free trial at pythonbites.fm/datadog and you'll receive receive a free T shirt.
08:02 KENNEDY: Nice. Yeah, they're doing good stuff. Thanks, Datadog. This next one Brian, you wanted it but I got it.
08:08 OKKEN: Yeah, you got it first. It's cool, I'm excited about it.
08:11 KENNEDY: Yeah, so there's a couple of cool libraries for building CLIs. Blake is probably the most prevalent one. We've got argparse. But people who do FastAPI, the cool new API framework, these folks have now gone, wait let me just double check that it's actually the same organization.
08:32 OKKEN: Yup.
08:33 KENNEDY: Yes, good, perfect. So they've gone and created a CLI definition library, like Click and I believe it's actually based on Click, Yeah, it's based on Click. So might wonder, well what does it do? The name gives you a little bit of a hint. It's based on Python type hints. So Typer looks at the various hints it can, you say like I have a function, it takes a name, which is, you know, name:str, it takes a string. And then you just use it 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. 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, that's pretty cool, huh?
09:16 OKKEN: It's super cool.
09:17 KENNEDY: Yeah, and then you can grow in more complexity, like you can have sub commands, you can have help on all the commands, you can actually get auto complete, you can install auto complete in your shell. All kinds of cool stuff. So you can create a little app, you say, here's a command and here's a command, and then of course the commands are functions, so functions take arguments, the arguments have types, they can have default values, all that kind of stuff, do you can try to run it, it'll give you help pretty much automatically, which yeah, this really speaks to me the way this works, I could definitely see myself using it.
09:49 OKKEN: Yeah, so a really good clean command line interface that has, if you're writing a tool with a command line interface, you're going to want help associated with it, you're going to want description of the different, the different methods and what they do, but also with as little boiler plate as possible, you don't want to like clutter up your code. And as far as I'm concerned, right now, Typer wins. There's like hardly any extra overhead that you have to add to make this work other than type hints, 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. But really clean, I'm totally going to start using this.
10:26 KENNEDY: It's really nice. And because it can leverage so much of the natural program itself, right? The argument names, the fact that it has arguments, the argument types, that's really helpful for it, yeah. So you can also integrate things like Colorama and Click, which this is based on, automatically start using color for its output, if you just have Colorama around, you can also use Click completion and Typer, this is the part I was talking about, will automatically configure to provide completion for all the shells.
10:56 OKKEN: That's really cool too. So the one thing I was concerned about is, it's based on Click, and one of the things I love about Click is that it has a good testing interface. You can write tests against your command line app as if you're going to writing from the command line, but you don't actually have to spawn a new sub proc to test things. 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.
11:23 KENNEDY: Oh, that's really cool. Yeah. Nice. Yeah, you don't have to start it. Basically shell out and see what happens right? Or like issue the command line directly to the shell to see what happens.
11:33 OKKEN: Yeah, really good job.
11:34 KENNEDY: Yeah the other thing I like about this is you could say, well we don't want the API of Click, so we're going to start from zero and rebuild our version of what this whole world looks like, but what they did is said, we're going to build a new interface for writing hints, but on top of Clicks so things like Colorama and the completion just plug in seamlessly. I think that's a cool lesson.
11:55 OKKEN: Yeah, definitely, also it's a smaller project for them to have to maintain too.
11:59 KENNEDY: Yeah exactly, exactly. Cool, alright. This next one that you got here is from Chris Moffitt, right?
12:04 OKKEN: Yes, and this is actually, so I'm hoping this is completely still accurate, I imagine it is. The interface for a matplotlib hasn't changed too awfully much, I know they've had some cool releases, but I want to talk about an article called Effectively Using Matplotlib. And this is a 2017 article, but in the middle of the introduction he says, "at first I think I was a little premature in dismissing matplotlib. To be honest, I did not quite understand it and how to use it effectively in my workflow." And I think that matches what my understanding in matplotlib as well. I mean when I first needed to put charts or graphs or something, of course you try matplotlib and then you're kind of hit in the face with the complexity of the interface, and 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. And then I went out and looked for other alternatives, but now I think the alternatives have their issues too, so I'm ready to go back and take a serious look at matplotlib. This article is a really good look at kind of understanding it and at least Chris' workflow how to use it. And one of the things I like is the description that, one of the reasons why matplotlib's interface, it might be a little confusing is because it's coming from, it's really got two interfaces. It does a MATLAB-like state-based interface, and I'm not a MATLAB user, but I know that a lot of people have come from MATLAB to matplotlib. And so that sort of interface is important to have that transition be easy for a lot of people. But then there's also an object based interface that makes a lot more sense to me, and that's Chris's recommendation is to use the object based interface instead. And so now after he's gone through it a few times and he has some recommendations and his recommendations are to, first learn the basic matplotlib terminology, specifically what is figure and axis and around those. And what's difficult for me, is that those are just normal words that I think I already understand them, but it needs to make sure I understand what matplotlib thinks those things are.
14:17 KENNEDY: Sometimes it's just hard to work with matplotlib, unless you find an example right? A lot of times what I, my goal is to hunt the internet for something that looks like I want, figured out if I can find the Python code that created it, and then then I'm good.
14:30 OKKEN: Yeah, he does have an example in this which is, kind of walks through at least one example. But if you really want to kind of get your head around it to start with using pandas with matplotlib. And so yeah, you start your visualization with a basic pandas plotting, and then add if you want it more complex, try Seaborn, and then if you want to customize it a little bit more, he said used matplotlib native stuff to customize the visualization. And I wouldn't have thought to use that workflow so that, that's kind of nice. 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, what all the axis 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. And that's often, the secret of programming anymore is to know what specific word to Google. So I think that's a good thing.
15:23 KENNEDY: Nice and also I want to remind folks, longtime listeners of pylustrator, not really sure about the pronunciation like illustrator but py for the first two words? I'll put a link into the show notes, and what it is is it lets you take a basic matplotlib plot, it launches a GUI where you customize the look and feel and then it will output the code that would have done that for you.
15:46 OKKEN: Oh cool.
15:46 KENNEDY: Yeah, we talked about that Episode 137 which is like a year ago or something. But anyway, still good stuff. I put a link in there for that.
15:52 OKKEN: I'm also including a link in a almost related section. Maybe there's an article about this, but I haven't found it. There's a Stack Overflow answer that has a little, the code you need to a stick a map, generate an a matplotlib graph, and put it in a Flask file on the fly, without having to save it to a file.
16:13 KENNEDY: Oh yeah, like a live dashboard type thing, that's cool. Nice, 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. Maybe you want to do some query, it updates 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, and it's going to take a little while and you don't want to just have it block on your main request. There's a a bunch of different things you could do. You could set up a separate server with like Celery or RabbitMQ or something like that, but that's a whole nother set of servers and it just gets way more complicated. Sometimes you want to just run it in the background, just in the same process, right? It's easy to share data cause you just pass the pointers. So there's this thing called Django Simple Task and it allows you to run background tasks in Django 3 without requiring any services or workers.
17:09 OKKEN: Oh cool.
17:10 KENNEDY: Yeah, basically the reason it has to be Django 3 is it's based on Async IO and Django 3 is the first version that uses ASGI. And so you actually need to run it in ASGI server, like Uvicorn or something like that. So be really aware that you have to run it in its async way for this to work. But basically when it starts, when the web app starts, there's a queue created and some workers to listen to the queue, and then you just go to any function you want to run and you just called defer on it and that'll kick it off in the background. And you could do that now, like you could create a thread and just launch it as a background thread, but one you don't get like a thread pooling, 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. So that's cool. And you can either give it a Async IO 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. So it's pretty cool and it's really easy to use, like ridiculously easy.
18:16 OKKEN: Oh, that's neat.
18:16 KENNEDY: Yeah. So there's a simple little example I put in the show notes where, it just imports the deferred operation method. It has two tasks, one of them which uses time.sleep that requires threading and one uses Async IO. Cause it's an Async method, so it would run on the Async event loop and in the view can just say defer one, defer method one, defer method two, and then do your regular work. And that kicks it off, but it doesn't wait for the response, but does it in a slightly more durable, better way than just like launching threads or kicking it off on on its own little background thing to go run unmonitored.
18:50 OKKEN: So in this example would 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 deferred tasks get run?
19:01 KENNEDY: They might get started, but because both of them in both of them, they sleep for a second to kind of, that's their work, right? They're sleeping for a second. So certainly the response would be sent back to the user before those are done.
19:14 OKKEN: And this is cool interface because the two tasks, one of them is Async and one is not. And the defer call doesn't, you can't tell.
19:22 KENNEDY: Right, you don't have to treat it differently. But if you can do Async, great, it'll do that on the Async event loop, I think. Otherwise it just puts it into a thread pool, which is pretty cool that you don't have to worry. You can just kind of go, here's some background stuff. It's IO driven, we'll do it this way. Otherwise just do it that way, which is, I like it. It's nice.
19:39 OKKEN: Yeah. Really nice.
19:40 KENNEDY: Cool, cool. Alright, what's this last one you got for us?
19:43 OKKEN: Well, so something happened recently. I've got a handful of, I don't know, open source, work on open source projects, really as much as I'd like to, I'd like to work, do some more work on open source projects, but I have a few that I maintain myself, but project is in air quotes I'm doing, because they're really pretty small projects, and a couple of them, cards and submark are things that I just created to help with teaching people how testing works. They're not really intended to be useful projects. And then another one was a plugin for pytest. Actually, I do use it and it's useful for me, but I started getting, like in the last handful of months, I started getting pull requests and issues and stuff, and I didn't know anybody was using it at all, so this was surprising to me. But I wanted to highlight, I dunno if, I can't believe we haven't covered this or maybe we already have, there's a website called, pypistats.org.
20:43 KENNEDY: Yeah, that's news to me, I hadn't heard of it.
20:44 OKKEN: It's just a simple interface. Now the back end might be complicated, but in the interface it just has like a little box and you plug in a Python package, and it tells you what the download stats, actually quite a bit of history too of different packages. And so I use that for all of my different projects, and sure enough cards was downloaded like I don't know, less than 400 times in the last month, submark less than 70 times in the last month. And then a pytest-check, which is a little app or a plugin that lets you have multiple failures per test, but keep going during the test, that was a downloaded just a little less than 20000 times in the last month, so clearly that one's more, and that's the reason why I'm having more people try and tell me what's wrong with it. So I guess I have to start maintaining.
21:37 KENNEDY: Yeah, this is a super cool interface, you get all kinds of graphs and the ability to slice it by dates and whatnot and yeah, I like it.
21:45 OKKEN: And there's also a top 20 so you can look at like, really who, what are the top 20 packages on PyPi and that's why I say that mine are small peanuts, because at the most it's like 19000 a month, but the top 20 all get over 1.3 million downloads per day.
22:02 KENNEDY: Yeah, and you can look at, look at the trends, that's pretty interesting. There's probably some cool stuff to go through here, and I guess if you were trying to do predictive stuff or analytics, there's probably a lot of things to be done here.
22:14 OKKEN: Yeah, definitely. It's also just occasionally, I'm just, the top 20 I like because I was, occasionally, I'm curious about that of like, what are the top packages on PyPi? What are people using?
22:26 KENNEDY: Yeah, it's, they weren't what I expected. I mean some of the ones that obviously expected were, but I think right now urllib was the top top, and I didn't expect that so. urllib3 specifically. But yeah.
22:39 OKKEN: It must be used by a whole bunch of other stuff like six, I don't think people are specifically downloading six. It's the 2 to 3.
22:45 KENNEDY: Like I think requests uses both of those and Certifi and that like hits the top four. So python-dateutil is a good one up there, botocore pretty interesting. I use Boto3 which depends on Boto4, but it kind of makes me frustrated, cause Boto3 won't use the latest version of Botocore, but it's all made by the same people, anyway. There's a bunch of interesting stuff up here. Yeah, I think I'm going to have to look at this some more.
23:12 OKKEN: pip is in the top 20.
23:14 KENNEDY: Nice, got to get started somewhere. Alright, well that's it for all of our main items, what you got for the extras?
23:20 OKKEN: Well, I wanted to actually just ask for some help. The last episode we put out, was a live recording of the January Python PDX West meetup. Actually not the entire meetup, but just a...
23:32 KENNEDY: Newsflash in case people haven't been paying attention and they're near Portland, there's a second Python meetup which runs out in Hillsborough. Much better for West side folks. So yeah, you put that together, good job.
23:43 OKKEN: Yeah and one of the things that we wanted do with this one was to, I got this from a lot of people in the tech community, well, the West side has a lot of, like high tech stuff that are, there's a lot of people coming from other languages to Python. And so they might not be beginner programmers, but they're beginners at Python. We've had this before, people saying some beginner friendly content per, at least one topic per meetup, so that people aren't lost with everything. And yeah, we kind of forgot that this last time, but I enjoyed the topics. I'd like to hear from other people as to what sort of, topics would be good at a meetup for Python beginner friendly, and just let us know, I think that'd be cool.
23:43 KENNEDY: Yeah, well it's easy to get super into, like focusing on what's the most latest, coolest, hottest thing, But there's a lot of folks who really just appreciate like setting the ground, lay a good foundation, and so yeah, I think that's a good topic.
23:43 OKKEN: Yeah and I, one of the things I'd like to do is possibly get a list of those sorts of things so that we can try to ask people to put together something. Like one of the meetups I did, like just describing how pip and virtual environment work and how to use those. And there's a lot of basic stuff that people don't even think about, maybe that they should talk about. And we could hear more. I also want to try to get other people to do it, because you and I could probably do those sorts of beginner friendly ones. It'd be fun to have other speakers too.
23:43 KENNEDY: Yeah, absolutely. It would be very fun if people were willing to get to Portland on a Tuesday evening, then they can come give a presentation.
23:43 OKKEN: Yeah! One more thing I want to do a shout out to PyCascades 2020. I can't remember when, it's in February coming up.
23:43 KENNEDY: February I believe, I believe it's like eighth and ninth, let me double check. It is February eighth and ninth in Portland, East side.
23:43 OKKEN: Wonderful. So people should get tickets so they could come and if you haven't bought your ticket yet, check the show notes, because we've got a discount code for 10% off.
23:43 KENNEDY: Yeah, absolutely. Yeah, that's right there at the bottom. I don't really have anything this week or extra to share, but one thing that did come out and catch caught my attention yesterday that I think, I might as well just throw out there is kind of cool is Firefox 72 is out and it has come with anti fingerprinting, which is pretty exciting. So I didn't know how many people are aware, I don't know if you're aware of Brian, that even with ad blockers and blocking like tracking cookies and stuff, there's all these weird things that you can do to figure out, actually it's that same person, right? Like you can ask, what are all the installed fonts? And what version of OS do you have? And here's a canvas thing, let's run some stuff on there and like try to detect weird CPU cycles and other kinds of, there's all these, like weird things you can do in browsers and pretty much uniquely identify, everyone right in our web browser,
23:43 OKKEN: Oh, that's lame.
23:43 KENNEDY: Even without cookies, yeah. So Firefox is trying to fight back against that. So I think that's pretty awesome, and worth checking out. I'll actually link to a ARS Technica article about it.
23:43 OKKEN: Okay, cool.
23:43 KENNEDY: Well I guess that leaves us with the joke, doesn't it? The last time we did a joke about describing different software programming jobs, this one, you're going to have to imagine that people who are fans of various programming languages are in like a literature course and they have to write an essay, okay? Have you, have you pulled up the picture?
23:43 OKKEN: Yes, I'm already laughing.
23:43 KENNEDY: Alright, cool, so basically there's professor looking person with glasses trying to read, like a paper, a physical paper and a student looking at it, getting the feedback from the professor. This is important because at the end it gets interesting with the pictures. So for Python it says, "This is plagiarism. You can't just import essay." For Java it says, "I'm two pages in and I have no idea what you're saying." Assembly...
23:43 OKKEN: That's pretty appropriate.
23:43 KENNEDY: Assembly, "Do you really have to redefine every word in the English language?" With C it says, "This is great, but you forgot to add a null terminator. Now I'm just reading garbage." I don't really know if I understand the C++ one, but it says, "I asked for one copy, not 400." And the professor's buried in books.
23:43 OKKEN: I don't get that either, but okay.
23:43 KENNEDY: Yeah, Unix shell, this one's pretty good. I don't have permission to read this. LaTex which you often use to describe like, scientific papers and like super elaborate mathematical formulas. It says, "Your paper makes no sense to me, but it's the most beautiful thing I've ever laid eyes on." And the final one is the HTML programmer and instead of holding a paper, the professor's holding a pot and they just say, "This is a flower pot."
23:43 OKKEN: I don't get it, but it's funny.
23:43 KENNEDY: Yeah, I don't either. Alright, so anyway, I thought that was pretty fun. You all can check it out, the comic is linked in there, but it's pretty fun. "You can't just import essay, this is plagiarism."
23:43 OKKEN: Alright, well thanks a lot Michael.
23:43 KENNEDY: Yeah, you bet, thanks Brian, good to be here.
23:43 OKKEN: Thank you for listening to Python Bytes. Follow the show on Twitter @pythonbytes. That's Python Bytes as in B-Y-T-E-S, and get the full show notes at pythonbytes.fm. If you have a news item you want featured, just visit pythonbytes.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.