#18: Python 3 has some amazing types and you can now constructively insult your shell!
#1 Brian: pdir2
- Nice use of animated gif to showcase what it does.
- It’s a replacement for
dir()
to use interactively. pip install pdir2
, butimport pdir
.pdir(something)
gives you all thatdir()
does, but splits things into categories like exceptions, functions, attributes, …- each item on one line, and includes the first line of the docstring for the item.
- Also, uses colors nicely. (Except I need to run it in a shell with non-black background on my mac or I can’t see the docstring output. )
- Hugely useful if you use
dir()
interactively. - 😞 Readme is in markdown, pypi still can’t handle that well. Maybe a listener can do a pull request on it to spiff up the pypi page: https://pypi.python.org/pypi/pdir2
- Consider pairing this with ptpython
#2 Michael: How to recover lost Python source code if it's still resident in-memory
- Ooops: I screwed up using git ("git checkout --" on the wrong file) and managed to delete the code I had just written, but it was still running in memory.
- Uses
- http://pyrasite.com/ Tools for injecting code into running Python processes
- https://github.com/rocky/python-uncompyle6/ A Python cross-version decompiler
- Main take-away: Really cool to attach pyrasite and explore a running Python process for many reasons.
#3 Brian: New Interesting Data Types in Python 3
types.MappingProxyType
- acts like a dictionary, but it’s read only. Convenient for exposing a mutable data structure through an API and making it less convenient for the client code to modify things they aren’t supposed to.types.SimpleNamespace
- kind of a general purpose class with attributes defined by the constructor parameters you pass in. May be useful in places where you would usecollections.namedtuple
.typ``ing.NamedTuple
- You define a class derived fromNamedTuple
, and define attributes with types and optionally a default value. Constructor automatically assigns the attributes in order.- These types help you to create quick small classes/types allow concise and readable code.
#4 Michael: howdoi
- Instant coding and shell answers via the command line
- Examples
- howdoi print stack trace python
- howdoi connect sqlalchemy
- howdoi python save dict
- howdoi debug python
- howdoi install ohmyzsh
- howdoi change path macos
- Notable related
#5 Brian: A python project from a listener of the show converts to asyncio and speeds up by 150x
- Project is a Python interface to a commercial cloud based CRM called Emarsys. But the specifics are kinda beside the point.
- Comment from episode 17: http://disq.us/p/1h43lc0 From Diego Mora Cespedes
_Another awesome episode, thanks Michael and Brian!_
_About asyncio being awesome, I had my own experience. I had to send information about hundreds of thousands of users to a CRM through their public API daily. Without asyncio, it would have taken 50 hours daily, which we all know is just not possible! After developing a sync (using requests) and async (using aiohttp) client for their API, I managed to send the information about the users to the CRM asynchronously, and it takes... ... ... wait for it... ... ... 20 minutes!_
_So that's 150 times faster than without asyncio!_
_Anyway, if you wanna take a look at the client I open sourced, here is the link: https://github.com/transcovo/pymarsys_
_Oh yeah, fun fact: the first time I implemented the async functionality, I made too many API calls at the same time, my mac crashed and I think I DDoSed the CRM. Now I use semaphores, which allow you to limit the number of tasks you launch at the same time. So much Python awesomeness!_
- However, I can’t find where in this code he uses semaphores, so here’s an example that does use semaphores to limit how many connections to make at a time.
#6 Michael: cookiecutter-pyramid-talk-python-starter
An opinionated Cookiecutter template for creating Pyramid web applications starting way further down the development chain. This cookiecutter template will create a new Pyramid web application with email, sqlalchemy, rollbar, and way more integrated.
- Factored and organized: This code was generalized out of a large, professional web application
- Master layout template: Comes pre-configured with a master layout template. Navigation, CSS, JS, etc is factored into a single template file and is reused across all views
- Chameleon language: This template uses the chameleon template language (the cleanest template language for Python - we did say opinionated right?)
- Pyramid Handlers: Code is factored into handler / controller classes. You have the full power of object-oriented programming immediately available
- Secure user management: The app comes with full user management. Users can register, log in and out, and reset passwords. We use the passlib package for secure user storage using best practices SQLAlchemy data access: Comes with SQLAlchemy ORM preconfigured using sqlite
- Bootstrap and modern design: As you can see from the screenshot below, the app comes with bootstrap and fontawesome. It uses a free, open-source theme and images used under CC-Attribution.
- Logging with LogBook: A logging system for Python that replaces the standard library’s logging module. It was designed with both complex and simple applications in mind and the idea to make logging fun
- Runtime error monitoring with Rollbar: Rollbar adds runtime notifications and detailed error tracking and it comes pre-configured in this template
- Mailing list integration: Comes with Mailchimp integration. Just enter your API key and list ID to start collecting and managing users for your mailing list
- Outbound email with templates: The app has a set of static HTML files with placeholders that are loaded by the outbound email system and populated with user data
- Bower static resource management: Most templates are based on out-of-date files (css templates, js, etc.). This template is uses bower for it's static files. This means a single CLI command will get you the latest everything.
- Fast pages that are never stale: Every static resource is referenced with our own cache busting system. This means you can use extremely aggressive caching for performance on static files yet they immediately invalidate upon changes
- Comes with an entire online course: This template is built from the final project in Python for Entrepreneurs, a 20 hour course on building professional web apps in Python and Pyramid from Talk Python Training
Episode Transcript
Collapse transcript
00:00 Hello and welcome to Python Bytes, Python news and headlines delivered directly to your earbuds.
00:05 I'm Michael Kennedy.
00:07 And I'm Brian Okken.
00:08 We're here to share the Python news with you and basically share the cool stuff that Brian and I found for the week.
00:13 Brian, you know what I think is cool?
00:15 That I can open up, I can basically go to any Python object or module or even function or whatever and just say,
00:21 tell me what is in you, dir this thing, and out pops all of its capabilities, mostly anyway.
00:27 Yeah, I love that.
00:27 Do you know what REPL stands for?
00:30 REPL?
00:30 REPL.
00:31 Read Val Print Loop.
00:33 Just what you get when you type Python.
00:35 Yeah.
00:35 Like most of the time now, the only time I open that is to try out something little or to actually to use the dir command.
00:44 You know what?
00:44 I do do that as well.
00:45 I'm like, I'm going to open this up and just do a dir here.
00:47 Okay.
00:48 Back to work.
00:49 Yeah, exactly.
00:50 Just to look at the interface for something.
00:52 So I was excited to see a new project from Leica 9M on GitHub called pdir2.
00:59 And when you use it, so it's pdir2 when you're installing it.
01:04 But when you use it, it's just pdir.
01:06 And it's just a replacement.
01:08 And it's cool because it's all colorized and it splits it out and splits it up into one line per item.
01:17 And it pulls the dock string out of each item so that you can see that.
01:23 And for what I'm using it for, when I pull up the REPL, it does what it should do automatically.
01:29 And I like it.
01:30 Yeah, it's cool.
01:31 I mean, it's kind of like if you typed help a thing, but it gives you meaningful stuff even if there's no real help to be had.
01:38 And it'll categorize things like, here are all the classes.
01:40 Here are all the functions.
01:41 Here's the stuff that I have no idea what it is, but it's also there.
01:43 So we'll put it over here.
01:44 And if there's dock strings, it'll put the dock strings there for like, here's the description of each thing.
01:49 It's really nice.
01:50 Yeah, and it does, it like puts all the dunder functions out of the way so those aren't in the middle of everything too.
01:56 Yeah, yeah, those land in the other.
01:57 Yeah.
01:58 Anyway, it's just, it's part of my repertoire now.
02:02 I'm going to, I'm never going to let it go.
02:03 One of the things I would like to ask somebody to do is to help out with the project and convert the README to whatever that other thing is.
02:12 Restructured?
02:13 Yeah, restructured so that it looks good on PyPI because it's in, the README is in Markdown.
02:20 And PyPI still doesn't like Markdown.
02:22 I wonder, did you look at it on PyPI.org?
02:25 No, I haven't.
02:26 No.
02:27 Versus the older one?
02:29 It's worth looking at that one.
02:30 I have no idea what it'll look like there either, but that's the new one.
02:32 Maybe it understands Markdown.
02:34 Who knows?
02:35 I would also like to recommend that people pair this with PtPython.
02:39 Do you know PtPython?
02:40 I don't.
02:41 Oh my God.
02:41 PtPython is awesome.
02:42 Like I don't use the REPL very much.
02:44 Everyone knows I love PyCharm and I kind of live there.
02:47 But when I do go into the REPL, it really annoys me that I need to completely type everything that is there.
02:54 That it doesn't autocomplete.
02:56 Especially when I'm exploring something new, which is like why we said often we go into the REPL.
03:00 Well, PtPython is basically, I think it's a little bit based on Emacs, but it's like a different version of the REPL that has code completion and a little bit of a UI over the terminal.
03:12 It's really nice.
03:13 So these days, if I want Python, I type PtPython.
03:17 Okay.
03:17 Well, I'll have to check that out.
03:18 And just to let people know, I did.
03:20 Okay.
03:21 So I'll take back that request because I looked it up on PyPI.org and it looks great.
03:26 It even has that animated GIF on there.
03:28 Oh, yeah.
03:29 So there's a little animated GIF that shows you how it works, which is quite cool.
03:31 So, yeah, check out this project.
03:33 It's easy to use.
03:34 The one thing to notice is you've got a pip install peter2 versus peter, but then the module's just pdir, right?
03:43 So there's a little bit of a nomenclature funkiness, but it is cool.
03:47 You know what else?
03:48 Go ahead.
03:49 I just want people to remember that.
03:52 And speaking of memory.
03:53 Speaking of memory, absolutely.
03:56 So this has happened to me once in my life.
04:00 I have not used these tools to solve it.
04:03 But here's the deal.
04:04 Like, suppose that you somehow, through probably your fault, but it could have been like a hard drive failure or something, somehow you've lost your source code.
04:16 So there's a guy who was using a source control wrong and wiped away all of his files off of his directory.
04:22 But he still had the program running in memory.
04:25 It was a Python program.
04:27 So he used two really interesting packages that I'd never heard of.
04:32 Had you heard of these?
04:33 No, I haven't.
04:33 One of them is Pyrocyte, like parasite, but for Python, P-Y-R-A-C-E.
04:38 And the other one is un-compile 6.
04:42 Because apparently it's one of the few that supports both Python 2 and Python 3, and multiplication is awesome.
04:48 So un-compile 6.
04:50 So the idea is with Pyrocyte, you can attach to any running Python process.
04:56 And you basically get this REPL, like we were talking about, to just explore its memory, to see what its values are.
05:02 You can even, like, force it to load other scripts and run them.
05:05 You can go to, like, an in-memory, like, singleton object and, like, work with its data.
05:10 All sorts of stuff you can do in real time while it's still running around.
05:16 That's pretty cool.
05:17 So basically what he did is he said, all right, I'm going to hook up to it with Pyrocyte.
05:21 I'm going to force the thing to load un-compile, and I'm going to call un-compile on itself and get my code back.
05:26 Wow.
05:26 But mostly the core.
05:29 It worked.
05:30 Yes, it worked.
05:31 There are people, there's, like, a whole bunch of people.
05:33 It's a, I get a gist.
05:34 At the bottom of the gist, they're like, oh, my God, I just had to try this because I couldn't believe it would work.
05:39 Oh, that's so awesome.
05:39 Yeah, it works.
05:40 I think there's either a video or a little GIF that is just, like, that basically shows you how it works if you go to Pyrocyte.
05:46 Pyrocyte.com, which that's the coolest takeaway for me on this whole one.
05:50 That's awesome.
05:51 Good job.
05:51 Cool.
05:52 Yeah.
05:52 All right.
05:54 You know what I think?
05:55 You and I were on a mission, right, to make sure that Python is modern Python, not legacy Python, right?
06:00 Yes, definitely.
06:00 And I was really surprised.
06:02 They're still adding quite interesting and new data types to Python 3.
06:07 Even stuff that's been around for a little while that I just haven't, like, heard of.
06:11 Because of this show, I've learned about the simple namespace as a, wait, no, that's not it.
06:18 The name tuple class, the capitals.
06:20 But let's get into this.
06:21 There's an article called New Interesting Data Types in Python 3.
06:26 And it just goes through three that I really like.
06:29 And the three types are types.mappingproxytype, types.simple namespace, and typing.namedtuple.
06:38 And the mapping proxy type is, like, really cool.
06:42 It takes, like, something like a dictionary.
06:45 Or it says, I can't think of anything other than dictionary.
06:48 It says it only takes mapping types.
06:50 Yeah, it's really cool.
06:51 But it's, like, a read-only dictionary, basically, right?
06:54 Yeah.
06:54 So you take a normal, like, maybe a normal dictionary.
06:56 And, like, if you had an API or something, you wanted to expose that dictionary through the API,
07:02 you could wrap it in this mapping proxy type and return that.
07:05 And I tried it out.
07:07 And so, for instance, one part of your project could use the mapping proxy type version and read everything.
07:14 And the part of your system that can have, that still has write access, that is synchronized.
07:20 So it isn't a copy.
07:20 You can, when one part writes to it, it gets updated in the other place, too.
07:25 Oh, it's like a view into an existing dictionary, huh?
07:27 Yeah, it's a read-only view.
07:28 Sweet.
07:29 So that's nice.
07:30 And then not too long ago, I started using where I really wanted classes or something.
07:36 And I was using things like tuples or lists where there really should have been something more structured.
07:41 I've been using the lowercase named tuple.
07:45 Right, out of the collections names, out of the collections module, right?
07:47 Yeah, out of collections.
07:49 And there is this thing in types called types.simple namespace that uses a similar sort of functionality.
07:57 You can just, if you define a type to be that type, you can just, the parameters when you instantiate it,
08:05 those just become elements inside the object.
08:07 It's pretty convenient.
08:09 Yeah, that's cool.
08:09 So you can do a little more organization.
08:11 And then, of course, the typing.name tuple class is really nice.
08:15 I hadn't been using it.
08:15 I've always just used collections.name tuple.
08:17 But I think I'm going to be switching.
08:19 Now, one word of caution, I suppose, is a lot of these are, they require pretty modern Python.
08:25 Some of the techniques they show require 3.6.1.
08:29 Like, 3.6.0 is not enough.
08:31 So just be aware that some of these require pretty new versions.
08:35 Yeah, and that's, I guess, it's hard for me to remember because I'm usually trying to stay.
08:40 I don't go into the in-development Python, but I try to use the latest released one.
08:45 I do as well.
08:46 You know where that bites me, though?
08:47 Is on my DigitalOcean servers.
08:49 It's running the version of Ubuntu that only has 3.5.2 or something like that.
08:55 So if it's literally a 3.6 feature, I'll put it in my app and I'll ship it like, oh, it doesn't work.
09:00 What was I thinking?
09:00 Yeah, and I forget that.
09:02 That 3.5 is really, for version 3.x, 3.5 is being used more than 3.6 right now.
09:09 Yeah, just because of the distributions, I think.
09:11 In my mind, that's like the minimum bar for 3.5 stuff is 3.5.
09:15 Yeah, okay.
09:16 But I think these are great things to start adding to your repertoire.
09:20 If you have control over where it's going to run, those are good things.
09:24 Yeah, and some of them come from like Python 3.3 or 3.4.
09:26 They're fine.
09:27 There's just a few techniques there that are brand new.
09:29 And what I like about this, one of the things I like about this article is it's pretty short
09:33 and it gives examples of how to use these pretty concisely.
09:37 So that's good.
09:38 Yeah, very nice, very nice.
09:39 Speaking of how do I use things, wouldn't it be awesome if I could just go to my Bash shell
09:45 and just type, how do I and type a thing?
09:47 Like I'd like to say, maybe I want to know how to print a stack trace or trace back in Python.
09:52 I could say, how do I print stack trace Python and just type that?
09:56 Yeah, that'd be cool.
09:57 It'd be cool if my computer knew that, right?
09:59 Well, it turns out we ran across a project called How Do I?
10:03 And if you install How Do I, if you pip install it, it will give you instant coding and shell answers on the command line.
10:11 So I can say, how do I connect SQLAlchemy?
10:14 And it'll give me the Python code to connect for SQLAlchemy.
10:17 I can say, how do I Python save dictionary?
10:20 That's three words.
10:21 And it'll show me how to use the JSON load s dump functionality right there in the command line.
10:28 Now, sometimes it gives me like a stupid answer.
10:30 Like if you type that, how do I Python save?
10:32 How do I Python load JSON?
10:35 It just shows you JSON, which is stupid.
10:37 But you can do a dash A and it'll give you all the answers it's found on the internet related to that.
10:43 Usually somewhere in that list is something decent.
10:45 It has some sources like Stack Overflow and I don't know where they all come from, but they're all over the place.
10:50 It's cool.
10:51 I can say, how do I debug Python?
10:53 It'll show me the PDB commands.
10:54 I can say, how do I change my path in macOS?
10:57 And it'll show me how to do that, assuming I'm running Bash.
11:00 Things like that.
11:00 Okay.
11:01 I'm going to try that.
11:02 Yeah.
11:02 How do I put a worm on a hook?
11:05 Exactly.
11:06 Yeah, it doesn't work.
11:08 Yeah, it's got to be something technical, I think.
11:10 There's another one that I want to point out that I thought was really funny,
11:14 but actually it's kind of useful.
11:16 Oh, what's that?
11:16 It starts with a command.
11:17 It actually starts with a cartoon.
11:20 And basically the idea is if you type in a command that you think you know on the shell,
11:24 but you get it wrong, you can curse at your shell and it will potentially fix it for you.
11:30 Yeah, I love this.
11:30 So this thing is called the F.
11:32 I'm not going to complete it so we can keep our clean rating in iTunes, but the link is in the show notes.
11:38 And basically you type that after you've made a mistake on the shell and it goes,
11:41 oh, you probably meant this.
11:43 Are you trying to do this get checkout branch thing?
11:45 You did a little bit wrong.
11:46 The F.
11:47 Oh, here's the command.
11:48 Enter.
11:48 And it'll just automatically fix it for you.
11:50 It's beautiful.
11:50 Yeah, nice.
11:51 So plus you get a little, yeah, you get to let some frustration out on the thing, right?
11:55 Yeah.
11:56 All right.
11:57 What you got for us?
11:58 What's next?
11:58 Oh, yeah.
11:59 I got to go back to that tab.
12:00 We have a, yeah, we're at a listener.
12:04 So, you know, it just seems like we just did the last episode.
12:08 Just a few days ago.
12:09 How did that work?
12:09 That we had a listener commented on the last episode.
12:14 Last episode, we did mention some feedback from a listener about async speeding up their
12:20 some work.
12:21 And we have another one from a comment this time.
12:24 And this is a project that a listener shared with us.
12:28 And we've got a link in the show notes.
12:30 But it's a project called PyMarsis.
12:33 And I wasn't familiar with eMarsis.
12:37 It doesn't really matter.
12:38 But it's a cloud-based CRM.
12:41 And it's a Python interface.
12:42 But I'll just read this.
12:44 The gist of it is it sped it up.
12:45 And this is from Diego.
12:47 Thanks, Diego.
12:47 Another awesome episode.
12:49 Thanks, Michael and Brian.
12:51 And if you say that we do something awesome, we'll totally put you on the show.
12:54 Okay.
12:55 About async.io being awesome.
12:57 I had my own experience.
12:58 I had to send information to about 100 information about hundreds of thousands of users to a CRM
13:05 through their public API daily.
13:07 And with the synchronous stuff, it would have taken 50 hours a day, which you can't do that.
13:14 And they added async.
13:16 And it went down to 20 minutes.
13:18 So that's 150 times faster, which is awesome.
13:21 Yeah.
13:22 They were using requests before.
13:23 And in order to update their CRM, it took 50 hours per day problem.
13:28 So they switched to the AIo HTTP client stuff that has async and await in 20 minutes.
13:35 In fact, he told us an interesting story.
13:37 He said, the first time I ran it, it opened so many connections that it crashed his Mac.
13:42 And so he actually had to implement a semaphore limiting system, which is pretty cool.
13:47 Yeah.
13:47 And I did like that he added that, that he added the semaphores.
13:49 But the GitHub link that he had put up, I didn't see any semaphore usage in there.
13:54 So I've also, in our show notes, linked another article that talks about using asynchronous I.O.
14:00 with semaphores to limit how many connections you're making.
14:03 Yeah.
14:03 No, thanks, Diego, for putting that up there.
14:05 Anybody who wants to have a comment on the show, you know, you can contact us on Twitter where we're at, Python Bytes.
14:10 But also, every episode has a discuss section at the end.
14:15 So if you want to talk about an episode, share a link or whatever, drop in there.
14:18 So what's the link for this episode?
14:19 Pythonbytes.fm slash 18.
14:21 Yeah, yeah.
14:22 There you go.
14:22 Cool.
14:23 Yeah, it's super, I made it super easy to find.
14:25 And do you know what would be super easy is if somebody would make an easy way for me to do a pyramid installation.
14:31 That's right.
14:32 Because, so the way I made that slash 18 work, and you can still go to like slash episode slash list or whatever, is I use constraints in the routing infrastructure.
14:42 And so one of the things that I've been doing a lot of work with lately is with building pyramid web apps and trying to really help people jumpstart what they're up to, right?
14:52 Like I can go to pyramid and create a web app, and I get basically a web app that runs, and here's the view function, and here's a template.
15:00 Go.
15:00 Go write the web app from scratch.
15:02 And I kind of wanted to help the world not start from scratch, but start from near the end.
15:08 So I've been doing a lot with cookie cutter.
15:10 More on that later.
15:11 But I decided to create a cookie cutter template that is an opinionated cookie cutter template for creating pyramid web apps that have most of what you need pre-built and pre-configured.
15:22 And factored the way you would for a large professional app, not for like a one-file starter thing.
15:28 Yeah, that's great.
15:28 So I created a cookie cutter thing called cookie cutter dash pyramids --talk Python starter.
15:33 And so this came out of my Python for Entrepreneurs course, but you don't have to have anything to do with that to play with this template.
15:38 So what you do is it comes out with like when you just say create the website, it'll create like a shared layout.
15:44 So there's one place where all your general navigation and look and feel goes.
15:47 It has handlers for class management.
15:52 It has like secure user management using things like pass lib and other techniques, bootstrap, logbook, rollbar integration, MailChimp integration, outbound, email integration, power for static file management.
16:04 All just the list goes on and on.
16:06 But it's very much closer to where you need to be for a professional pyramid app to get started, in my opinion, rather than like basically here's one file.
16:16 Now create the whole thing from scratch.
16:18 So if people want to check that out, that's on GitHub.
16:20 You can just cookie cutter space the GitHub URL and you're off to the races.
16:23 It needs a little bit more documentation.
16:25 I'm going to work on that this week, but it's really close.
16:27 But it's a good start and I'm actually pretty excited about it.
16:31 And you've got one, your course is one of the very few that I've seen that really teaches people how to do pyramid as well.
16:38 Yeah, thanks.
16:38 Yeah, it's definitely one of the few ones on pyramid, which is great.
16:41 So here's like one more resource.
16:42 Basically, this is going to be part of the end of that course.
16:46 And, you know, the first time I heard about cookie cutter, I really kind of thought it was a little silly.
16:51 But it's actually turning out to be quite a useful tool for a lot of spaces.
16:56 Yeah, I definitely.
16:57 Yeah, the more I use it, the more I like it.
16:59 So I've definitely been putting some effort into it.
17:02 It's great.
17:02 Well, that's the end of our six.
17:04 So do you have anything to share with us, Michael?
17:07 No, nothing.
17:08 Nothing other than this template thing.
17:10 That's really all I have to share.
17:11 I've been just cranking on my classes.
17:12 I hear that you're probably still writing on your book.
17:16 That's getting close, though, isn't it?
17:17 Yeah, it is.
17:18 It's getting a lot closer.
17:19 And I wish it was closer than it is.
17:21 But I've got a whole bunch of...
17:24 Actually, I'm kind of overwhelmed with both work and with gratitude.
17:28 That I've had a lot of great people, incredible programmers and good writers help me out with
17:34 technical reviews on the first four chapters and a lot of great feedback.
17:38 And now I've got to go rewrite everything.
17:39 Thanks a lot, guys.
17:40 Thanks a lot.
17:42 No, that's awesome.
17:42 Yeah, I'm looking forward to the day where we can announce your book on here.
17:45 That'd be great.
17:45 Any day now with that book, huh?
17:47 No, any...
17:48 Hopefully, the goal still is to have something to sell by PyCon.
17:52 Beautiful.
17:53 All right, well, thanks, Brian, for sharing your news.
17:57 And thank you, everyone, for listening.
17:58 We'll catch you next week.
17:59 All right, thank you.
18:00 Thank you for listening to Python Bytes.
18:03 Follow the show on Twitter via at Python Bytes.
18:06 That's Python Bytes as in B-Y-T-E-S.
18:09 And get the full show notes at pythonbytes.fm.
18:12 If you have a news item you want featured, just visit pythonbytes.fm and send it our way.
18:16 We're always on the lookout for sharing something cool.
18:19 On behalf of myself and Brian Okken, this is Michael Kennedy.
18:23 Thank you for listening and sharing this podcast with your friends and colleagues.