Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to your earbuds.
This is episode 195, recorded August 12th, 2020.
I'm Michael Kennedy.
And I am Brian Okken.
And this episode is brought to you by all the cool work that we're doing.
Tell you more about this.
Right now, I kind of just want to think about paying attention to stuff, Brian.
Maybe watching things.
I'll, like, look closer.
Yes, watch carefully.
So, actually, this thing sent over by Preyson Daniel, he sends us a bunch of good topics and links.
And this one's called Watchdog.
And maybe you've heard of it.
It's the foundation of some web frameworks, for example, and things like that to know if, say, a file has changed that you're editing.
Does it need to auto-restart the website?
So when you refresh it, it actually reruns it.
Things like that.
But Watchdog is a cool little library that you can use just on your own to know if something has changed.
So, basically, it's a simple little API.
You create this thing called an observer.
And you tell it to just start watching in some way.
You give it a path.
It can recursively look.
You can give it, like, a pattern or whatnot.
And it will say, just basically start firing events back on this observer thing when stuff happens.
File is created.
File is deleted.
File is modified.
And so on.
Nice.
This is cool.
Yeah.
Isn't that cool?
It also comes with a CLI script called WatchMeDo.
And what you can do with WatchMeDo is just type WatchMeDo, all one word, log.
And wherever your current working directory is, when you type that, it will just start watching
for files that change right there, just like on the command line.
Oh, okay.
Cool.
So, if you're, you know, you don't want to write a program, but you just happen to be somewhere
and you're like, what is happening?
Like, what files are being modified or being touched or being changed here?
And you can just type that and boom, there it goes.
That's cool, right?
That actually is really cool.
Yeah.
We have a build process that part of it is mucking up some directories and it'd be kind
of cool to use something like this.
Yeah.
Absolutely.
And so you could just pip install WatchDog and then just type WatchMeDo space log wherever
you want it to know.
There's a bunch of other things it does that you can pass commands and stuff, I suppose.
Like, you could ask it to recursively watch or whatever, but yeah, I haven't had a use
case for that.
So, but yeah, this is a cool recommendation from Preycin.
Yeah, nice.
The example that I think I could possibly use it for is, you know, one of the things
that we do that makes Python Bytes, .fm, as well as all the Talk Python, Talk Python training
sites ridiculously fast as a user is we don't require you to re-download almost any of the
static content, anything JavaScript, any image in the entire site, you know, CSS, all of those
things, they're cached for a year, right?
And so a problem with caching stuff for a long time is if you redeploy a new version,
something's wacky, right?
Something is really weird.
Like, I have this problem with Twitter and Firefox.
I'll go there and periodically it'll just say something went wrong, can't load it, and a command
R, a hard refresh always fixes it because there's probably some JavaScript file that's like out of
sync with some other part of some API or some random thing like that.
And so what we do to make sure that never happens is we look at every one of those static
files that we have a year-long cache on and we read it and we create the hash and we put
question mark hash equals the hash of the file on the end of it.
So that, you know, that's a separate file.
If it ever changes one character, if it changes, the hash changes and it's now a totally separate
thing in the web browser cache, right?
All of this is perfect.
The one drawback is to make things fast, you're not rehashing all the images on every
request.
Is it just says, have I seen this file before?
Do I already compute the hash?
Just use it, right?
So that's fine.
But that means that little hash refresh trick only really works if I restart the website.
If like, say, only a CSS file changed.
I could use this watchdog to watch all the static files that we're hashing.
If any of them change, just instantly recompute the hash.
That's it.
So that would allow me to do like push deploys of just, you know, static content changes without
kicking the website at all.
And it would just magically like redetect those and start rolling.
I think I might start using watchdog for that.
Anyway, that's my use case that makes me excited.
And I'm glad you pronounced watch me do because I looked at it and I went watch meadow.
That's a weird name.
That is a weird.
I think it's a watch me do.
Watch me do is better.
Yeah.
You know what else is weird?
Weird HTTP status codes.
Like I'm very familiar with 400 bad requests.
That's when somebody's created an API and I talked to it badly and they've created the API correctly.
Or I get a 500 where there is their stuff has crashed because I sent them something bad and
they've written their API badly.
But somewhere in between there lives some odd thing.
Right?
Yes.
Have you heard of error code 418 before?
I'm a teapot.
This is just great.
So I love it.
I love it.
Status code 418.
I'm a teapot.
Any attempt to brew a coffee with a teapot should result in an error code 418.
I'm a teapot.
The resulting entity may be short and stout.
So the resulting entity may be short and stout.
I love it.
This actually got brought up in a conversation I was having this morning.
A colleague of mine, Andy Howe, suggested it.
He said, so when is Python 3.9 going to come out?
Because hasn't it been a while?
And as a reminder to everybody, Python 3.9 candidate release or the release candidate one
or RCCR or whatever is out now.
So you can play with it.
It's probably, I've been using it.
It's safe to use.
But the schedule for the release, final release is in October, I believe.
And then after that, bug fixes, releases every month are planned, if there are any.
Andy said, you know, my favorite enhancement for Python 3.9 is this 418.
I'm a teapot.
This is new in Python 3.9.
HTTP library was missing the status code 418.
I'm a teapot.
And now it has it in there.
But wow, I've got some links for you.
Because in the show notes, this is referenced from HTCPCP, which is Hypertext Coffee Pot Control Protocol.
And this came out in 1998 as an April Fool's joke.
And then it's kind of part of it now.
So it's part of HTTP, sort of.
Another fun error code.
So the document, the entire document for HTCPCP, says that most error codes share the same status codes as HTTP.
However, 418 is separate.
But also now HTTP can use that also.
But also 406, not acceptable.
So 406 is this response code may be returned if the operator of the coffee pot cannot comply with the accept addition request.
Unless the request was in ahead.
So, and it should return the list of possible coffee additions.
This is bizarre that this is in the world.
This is awesome.
I really love it.
I don't realize, I didn't realize it had been taken so far as this.
This is great.
And, you know, if you go and look at the thing you link to, the Hypertext Coffee Pot Control Protocol, HTCPCP slash 1.0.
The whole memo describes so much of this thing.
And it even talks about things like crossing firewalls.
Like, modern coffee pots do not use fire.
However, a firewall is useful in protecting it.
It also addresses the security considerations.
Anyone who gets between me and my morning coffee should be insecure.
Filtration is not a good virus protection method.
I mean, it just goes on and on.
It's beautiful.
Yes.
And there's a security layer that I forgot about that's funny, too.
That's part of this.
But I can't find it right now.
Yeah, no worries.
I threw in a quick link also to this place called HTPstatuses.com, which this is a slightly more serious take, although it does, of course, include the coffee pot.
But it's just, if you were like, what the heck should I be doing, you know, in this situation?
Like, here's the error codes here.
So, and if you just click on them, it gives you a, like, really nice summary.
Like, if you click on 403, for example, it talks about when it's used and when it's, like, how it compares to 404.
And then also how that reference code is set up as an enumeration in different languages.
So, for example, in Go, it's HTTP.status forbidden.
In Rails, it's colon forbidden.
And then Python 3, it's HTTP.htpstatuses.forbidden.
And things like that.
So, it tells you, like, the language version instead of typing it all in yourself and having magic strings and numbers everywhere.
Yeah.
So, it does look like, I don't know what Symfony is, but Symfony and Go do also support 418.
Status team.
Beautiful.
We're on par with Go in terms of the internet now.
Very good.
That was a good one.
It almost could have been the joke, but I like it.
All right.
Also, things that I like is that we get a chance to create amazing stuff to help people get better with Python out there.
So, over at Talk Python Training, we have three new courses coming very soon.
We have Getting Started with Data Science.
We have Moving from Excel to Python.
And we have Diving into Python Memory Management and Optimizing Your Programs around that.
All three of those are coming within probably a month or so.
So, if you want to get notified about that, just visit training.talkpython.fm.
And right at the front, there's a little email place you can enter your email address to get notified.
How about you?
I wanted to highlight Test and Code.
So, pytest 6 is out.
So, Test and Code is the other podcast I do at testandcode.com.
And I had talked last week that there was going to be a pytest 6 episode.
And instead of doing it just by myself, I contacted Anthony Sotilli and had him come on the show.
And so, we BS about stuff for about an hour.
Some of it's actual content.
So, I'm really trying to get – I'd really like to have the shorter episodes on there.
But, you know, I get to talking with somebody and it's just fun.
Just keeps going.
Awesome.
Yeah.
It's definitely a good one.
People should check it out.
What do we got next?
We talked about Pydantic.
Right?
So, Pydantic is a cool library.
You know, it's something I really just need to start using.
Like, I haven't created any new projects that probably really deserve this.
But I think it's definitely one of those things that I want to start using.
Because it's just so slick.
The ability to say, I have these models.
They have these types.
You can validate them.
You can auto-convert between them.
And so on.
Right?
So, we talked about that not too long ago, actually.
However, Andy Shapiro sent over a heads up about a new feature that's in the beta version of Pydantic that's coming.
That makes me pretty excited, actually.
I don't know.
We'll see how people feel about this.
But I'm excited about it.
I'm excited because I'm a fan of type hints.
Yeah, me too.
I love type hints.
Yeah.
I love them mostly because the way they make the editor help me write code.
Instead of going and saying, oh, how do I do this thing with this library?
Right?
If I just say the type of this thing is one of the objects out of that library and I hit dot, boom.
The editor shows me all the stuff I can do.
It just keeps me in flow and working on what I need to.
So, I love type hints mostly for that, but also for the validation.
One of the things they are like, but they do not do, is they are not like static typing in other programming languages where somehow that verifies what's being passed.
Right?
Yeah.
So, like, for example, if I have a function, it says it takes an integer, an age colon int, and that's what it says it takes.
But then I go and I write code and I give it quote seven instead of the number seven.
That's fine.
Python's like, yeah, that's cool.
You probably don't know what you're doing, but whatever.
We're just going to let it fly, right?
Well, with Pydantic, there's this new type annotation validator.
And so, what you can do is you can say, for this function, it has type annotations, and I want those to mean something.
Oh, okay.
Okay.
So, it gives it basically that it's not runtime behavior in the static languages.
It's a compile error, right?
Your argument string cannot be converted to enter whatever the compilation error happens to be.
But it would give you the runtime equivalent.
So, all you have to do is you have a regular Python function, and it has regular types, just like it would, you know, s colon str, count colon int, that type of thing.
And you can just work with it.
It's a little bit smarter than the compiler, maybe of, say, like C++ or C#, though, in that what you do is you just say, at validate arguments.
So, you give it a decorator that validates the arguments.
And not only does it validate, but it will convert if it can.
So, for example, I'll put an example in our show notes.
There's a function called repeat.
It takes a string and a number, and it's just going to echo out that string however many times that number exists, right?
So, you can say repeat, quote, hello, comma, three, and it'll print hello three times.
Super simple.
That passes the validation precisely because it takes a string and an integer.
But you can also say repeat, hello, comma, quote, four, and it'll still print out hello four times because it can take the string four and make it an integer four.
Oh, that's cool.
Right?
But if I say repeat, hello, comma, goodbye, boom, exception, validation error, you know, the value is not a valid integer, type equals type error integer or whatever, like some message there, right?
So, it says the count parameter is not and cannot be converted to the type specified by the type annotation.
Oh, yeah, this is cool.
How do you feel about this?
I obviously don't want runtime typing for all of Python code.
It's not, I don't want it.
But for special places, like maybe around your API.
Exactly.
The boundary of libraries, like stuff going in there.
Yeah.
You're like, please don't send me the wrong data.
Like, what is your other alternative, right?
If you really want to validate that, you have to do is instance of an int or try to cast it to an int.
And you've got to raise your own exception type and all that kind of stuff.
You're already going to have to do that work if you're writing a good API.
Or you're going to send out weird errors like int does not have, you know, type int does not have function split or some weird thing.
And people have to realize like, oh, what that means is you type some kind of, you send a number where a string value is accepted, right?
So this, if you really do depend on the types that you specify in your type annotation, this seems like a good idea to me.
The one drawback is this cannot make your code faster.
Right?
If it's doing both validation and type conversion in front of your code, it can't be faster than just not doing that.
But it might make your development faster.
It's not always about speed.
Exactly.
And it may make your sanity.
Yeah.
So one of the ways people have gotten around this in the past is instead of, is like for an example, doing, instead of trying to validate parameters to a function at an API level,
I have the parameters bundled into like an adders object and have adders validators written because adders does a...
Right, right.
Put it into a validated object type and then pass that thing over.
Yeah.
Gotcha.
Which is kind of what this is.
Yeah.
But without doing it.
This is cool.
Yeah.
I like it.
Yeah.
Very cool.
I like it too.
I was leaning Pydantically previously.
This makes me lean more that way.
I like it.
All right.
So one of the things that's really cool is, you know, we've had a lot of conversations about is Python fast.
Is it slow?
Is it fast for coming up with programs that work?
Does it execute fast?
Should you use libraries like Cython or rewrite some stuff in Rust?
But Anthony Shaw, he took it to another level, like further down than even something like rewrite bits in C or compile Python to C or something.
Yeah.
I think this is maybe an example of Anthony just having too much time in his hands.
I think maybe.
So Anthony wrote a, I just tried it this morning, a project called PyMult, P-Y-M-U-L-T.
You can pip install it.
And it just multiplies numbers.
And it only works for positive integers or negative integers.
It just doesn't do, it doesn't ever display negative numbers.
Anyway, regardless of that, it's an extension for Python written in assembly.
Because, why not?
And because of Anthony.
As in like MV and, you know, add like the assembly language.
That's the foundation of basically every other programming language?
Yeah.
In Anthony's Twitter announcement, he says, after a series of highly questionable life decisions, my Python extension written in pure assembly is now on PyPI.
It required an assembly extension for disk utils.
He also wrote a GitHub action support.
So it's running CICD and testing with pytest above and beyond, over the top.
But there is some coolness of it.
So it's a proof of concept to demonstrate how to create a Python extension in 100% assembly.
And then how to write a, you know, how to link those up.
How to call a C API and create a PyObject and parse PyTubule and stuff like that.
Basically all the stuff you have to do to get point parameters back and values out of an extension written in assembly.
And yeah, it's interesting.
So I like it actually.
Yeah.
And anyone wants to know what the code looks like.
It's like move racks comma X, imol, qword of Y, move result to racks, move eddy to result.
Like it's, yeah.
But then you get a call pylong from long, which is kind of a cool thing.
So there's this like interesting mix between the just pure assembly language and the C data types of CPython.
On a serious note though, anybody wanting to learn a little bit of assembly or something, it's not like often you need some sort of environment to try it out in.
Having some way to link Python and assembly is kind of neat actually.
So I'm grateful for that.
You're right.
Some of these commands.
I mean, I wrote some assembly in college.
That was a long time ago.
Yeah.
Well, you had to, Anthony, because that's some impressive stuff.
Yep.
Would you say it's easy though?
No.
That's hard mode.
Yeah.
You know what actually is not as easy, I think, as it should be.
And it's honestly just bizarre.
Our properties in Python, as in at property, some function return some value converts what looks like a function call over to something that looks like field access.
Right.
Yeah.
But why does it have to have this bizarre sequence of conventions where I have an at property and then a function that has a name?
Okay.
That defines the name of the property.
That makes sense.
But then the subsequent thing, if I want to be able to set that, I have to say, you know, if the property was a, then I have to say decorator a dot setter and also have a function that is called a.
Like the a dot setter stuff is weird.
And then the order of the setter varies as well.
Right.
It has to be, or is constrained.
It has to be after the property a.
And what I've found also is that you can run into issues with inheritance.
I think if the property is defined in the base class, you try to create a setter on a drive class, things don't work right as well, which is all kinds of weird.
Right.
So there's just like, this is supposed to make life easy.
Why is this so complicated?
Like surely there was just another alternative that was like easier to do, to implement as well.
Anyway, it's always kind of been one of the bizarro things of Python.
That said, I love properties.
I love that you can do like lazy calculation with them.
You don't have to store them.
It's part of the memory.
If they can be recomputed from the values, there's a lot of good reasons to have them.
So Rude Vanderham sent over this cool project, which he created called easy property.
And do you know what?
It's easier than regular properties.
Okay.
I think that's why he named it that.
So the idea is, you know what you could do instead of have this property and then at a dot setter or a dot deleter is you could just say there's a at getter, at setter,
at deleter on the thing.
And it doesn't matter what order they are or if they're all defined.
Like for example, you can't have a setter without a getter.
Now conceptually, you should never really do that.
But just, you know, syntactically, like those things have to be defined there.
And like I said, the sort of base class, drive class type of thing, it gets wonky as well.
So with this, you just create a function.
The function name is the name of the property.
And then you put at getter on it.
And that makes it the kind that is, you know, the getter property.
If you want to have one, you can set, you say at setter.
The order doesn't matter or anything like that.
Nice.
Isn't that nice?
Yeah.
So there's a cool little example that he wrote.
You know, you can do them separately, which is probably what I would recommend and do.
But you can also have an at getter setter.
So you can have one function whose job is to both be the getter and the setter operation of the property.
And then the way it works is just the value is set to be like some default value, which is what happens in the, you know, in the get version.
You can have an at documenter.
So you can also have documentation for your property.
And there's a little function that will come up and do that.
So anyway, it's, it looks pretty nice to me.
It's not one of those things that's going to be, you're taking a heavy dependency upon to give it a try.
Right.
It's not like a, a runtime thing.
Like for example, that validate arguments, that's in between every time you call a function all the time, all the time.
Right.
So like this is the type of thing, like if it's going to work, it's going to work clearly, or it's just not going to work at all.
So give it a try and see if it makes you happy.
And if it sparks joy, you can have an at getter.
And I like the syntax better than properties.
Just more sense.
Yeah.
And you know, I think at property is just fine.
Like, okay, that totally.
But you know, at property name dot setter, like that just drives me.
It's just so bizarre and weird in the ordering and the dependencies of it existing in the same class hierarchy level.
Yeah.
Don't get me started.
So this is cool.
I like it.
Thanks for developing that and sending that over.
So we talked about assembly and properties and decorators and stuff.
So if anybody's left listening to this podcast, we do have more to talk about.
Let's talk about recursion.
No, just kidding.
I couldn't resist highlighting the last topic.
So there was an article by Ryan Howard that was on the test project.io blog called non-blocking assertion failures with pytestCheck.
And I had to highlight it because it's really the first time anybody's ever written an article about something I wrote.
So that's neat.
Yeah, very cool.
So pytestCheck is a library that allows you to do multiple failures within a test, mostly because assert normally uses asserts, but asserts stop after the first failure.
And sometimes you want more.
And I never really thought about the different use cases.
But so Ryan has the use case of using it with Selenium because sometimes when you're testing a page coming back, you might have to test multiple aspects of it.
His example was what's the content of some field and also if the URL is correct or something.
And I think with like a Selenium or some sort of web test, that totally makes sense because you'd have things like you want to check the error code and content and whether somebody's name shows up, then a whole bunch of stuff you could check and having multiple things.
Right.
You don't necessarily have just one assertion that captures all of the essence of what you're trying to determine, right?
Yeah.
And so I also linked to the pytestCheck library and then or plugin.
And then also an article that I wrote back and way back in 2015 where I started thinking about this and thinking how to solve it.
Okay.
Yeah.
Very cool.
Nice article, right?
And then I also wanted to do a public service announcement that because both Ryan and Anthony got this wrong, there are no capital letters in pytest.
So.
Hold on.
Let me try this.
Let me just.
Yeah.
Okay.
On this one.
But if I go over to Word and I type pytest is a testing framework, I'll bet you that Word makes it capital.
Yeah.
Doesn't that make it real?
No.
I know PowerPoint well.
So.
No.
Yeah.
That's always tricky, right?
When you have sort of a formal name, but the formal name doesn't have spaces or a slower case.
But I'm fine with it.
We got used to it with iPhone.
So there's no capital.
I mean, the P is capitalized in iPhone.
Yeah.
Will Word correct you, I wonder, if you try to type iPhone?
I would doubt it.
I mean, but that's Apple and they think differently.
That's what they tell me.
But there's others.
I mean, so there's some, I guess it's a weird thing with tools and stuff.
Some tools don't care whether you capitalize or not.
The people in pytest like it not capitalized.
So anyway.
Yeah.
Let's respect their lowercase p's.
Yeah.
All right.
So I have three really quick things.
They're all kind of fun.
So PyMC is a cool library for Bayesian analysis and probabilistic programming in Python.
So Alex, one of the core developers there, sent me a message, said, hey, we're planning the first ever PyMC on.
Okay.
I pronounce PyMC on because it's so on, they say.
And it's the first Bayesian community online conference around PyMC.
So if you are into probabilistic programming in Python, check it out.
There's a bunch of cool stuff they got going on.
They're also, I think, have a call for paper.
So if you want to do a presentation, shoot them a note.
And yeah, I'll link to that in the show notes.
Second, a while ago, we talked about Rumps quite a while ago.
Rumps is ridiculously uncomplicated Mac, I don't know, something, menu programs or something like that.
And so I ended up, I was sitting around, I have this little library that I run because so many things in my life require taking a title or taking some words and turning them into a file name or a URL name.
All right.
So suppose I've got the title of a video and I want to name an MP4 file that.
And it's got like a colon in it or like a forward slash or some random thing that shouldn't be in there.
I don't want spaces.
I want it.
So I wrote this little command line utility called URLify that I would just run.
It would take whatever's in the clipboard and replace it with that canonicalized version that would work well as a file name.
Oh, cool.
Yeah.
And that was cool.
But then I got tired of like always firing up the command line, the terminal.
Maybe it's busy doing something else.
So I got to do a new window, run that thing and then close it down.
And I was like, I just want to click something once.
All right.
I don't want to click the terminal and then start a new terminal.
And it was like not that much work, but you can imagine the way I'm going on and on about this.
I must be doing it more than is reasonable.
So what I did is in like 45 minutes, I converted that command line app to a Mac dot app file, like a full on dot app file that I could just ship to any Mac user.
They have no idea.
It uses multiple Python packages, a code that I wrote, and it runs as a GUI auto starting with my macOS when I log in in my menu bar.
Oh, wow.
Is that cool?
Yeah.
And so I linked to a little tweet that has a screenshot just says convert text.
But actually, I changed it to URL fi text, trim text.
So now I can just click up there and any text that I have, you know, if you copy something and you want to like paste an email or like, but maybe it's got a bunch of white space.
You're like, why is this all here?
Right.
Instead of putting in text editor and just getting the little bit out.
I just hit that and then I'll trim it or like a good lowercase text.
So I have these like little cool like clipboard text transforms and it all took like 45 minutes to turn it into a Mac app with pi to app and rumps.
Oh, wow.
So are you sharing this somewhere?
I will happily share it.
Okay.
But I don't have the GitHub repo public yet.
So let me just like put like a screenshot and what the heck it is and then I can make it public.
But yeah, so I'll put it in the show notes.
Neat.
Yeah.
It's super cool.
And finally, I just want to let people know who are taking courses over at Talk Python.
I was sitting around and I decided I want as you hover over the scrubber, I want it to show show a little thumbnail of where you would go in the video if you were to click the scrubber in that location.
What's the scrubber?
We're not like the little like time thing where you can click around and like zoom ahead and whatever.
And we're not like reusing Vimeo or YouTube or something that might potentially do that.
I think YouTube does it.
I don't know that Vimeo does.
Anyway, I recently figured out how to do that.
So if people are interested, they can check it out.
But there's a cool little trick where you can take a second hidden video player and point it at either the same video or a smaller version of the video.
And then as the mouse moves, just seek it to that time and show it above where the mouse is.
Isn't that crazy?
Yeah.
So if people need something like that, yeah, a little cool trick you might consider.
Not that often in Python, but if you happen to make your way to the JavaScript side of things, you'll do a lot of that, I guess.
So is that in your training site?
Has it been updated then?
Yeah, exactly.
It's not updated as we speak because I'm still busy transcoding 200 hours of video to 500 by 200 size.
So I'm about halfway through that.
I've had yesterday I uploaded 200 gigs of data.
I'm so going over my data limit this month.
That was 20% of my data limit in one day.
I got more to send.
So, but yeah, it'll be there as soon as the videos are done.
It'll turn it on.
I'm going to back up a little bit.
This IMC.
Yeah.
Probabilistic programming.
Can I use this to make a probability drive?
A probability drive?
Yeah.
Is this from science fiction?
I don't know it.
Like Hitchhiker's Guide to the Galaxy.
Oh, no.
Yeah, I would think so.
I would begin by feeding it the number 42 and see what comes out.
Maybe.
Yeah.
That's probably the natural first step.
Yeah.
All right.
Speaking of jokes, we've got two.
You want to go first?
Yes.
This was submitted by Reuven Lerner, inspired by Anthony Shaw.
I used to do low-level programming.
Then a product I bought told me no assembly required.
Since then, I've been coding in Python.
Nice.
Except for Anthony.
Anthony's coding in assembly.
He codes in both.
Yeah.
His Python is assembly code or something like that.
So this one, like last week, we talked about little Bobby Tables, who's beautiful, over at XKCD.
And I've got another XKCD for us.
But this one is not about databases.
No, it's about source control and Git.
Okay.
All right.
You want to be the woman developer that asks the question?
Yes.
I'll start us off.
All right.
So there's a couple developers speaking.
First one is talking about this new source control.
This is Git.
It tracks collaborative work on projects through a beautiful distributed graph theory tree model.
Cool.
How do we use it?
No idea.
Just memorize these shell commands and type them to sync up.
If you get errors, save your work elsewhere, delete the project, and download a fresh copy.
It's funny because it happens too often, I think.
Yeah.
I think most people have like the four or five or six Git commands that they use all the time and everything else they have to look up if they need it.
Yeah.
Merge conflict.
All right.
I'm deleting it.
I'm going to copy this back over.
Yep.
All right.
Funny indeed.
Well, thanks for being here as always.
And thanks to everyone for listening.
Thank you.
Bye.
Thank you for listening to Python Bytes.
Follow the show on Twitter via at Python Bytes.
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.
On behalf of myself and Brian Okken, this is Michael Kennedy.
Thank you for listening and sharing this podcast with your friends and colleagues.