WEBVTT

00:00:00.001 --> 00:00:05.300
Data validation and conversion is one of the truly tricky parts of getting external data into your app.

00:00:05.300 --> 00:00:09.740
This might come from a REST API or a file on disk or somewhere else.

00:00:09.740 --> 00:00:16.120
It includes checking for required fields, the correct data types, converting from potentially compatible types,

00:00:16.120 --> 00:00:21.220
for example, from strings to numbers if you have quote 7 but not the value 7, and much more.

00:00:21.220 --> 00:00:24.980
Pydantic is one of the best ways to do this in modern Python,

00:00:24.980 --> 00:00:30.260
using data class-like constructs and type annotations to make it all seamless and automatic.

00:00:30.260 --> 00:00:34.080
We welcome Samuel Colvin, creator of Pydantic, to the show.

00:00:34.080 --> 00:00:37.880
We'll dive into the history of Pydantic and its many uses and benefits.

00:00:37.880 --> 00:00:43.320
This is Talk Python To Me, episode 331, recorded April 14th, 2021.

00:00:43.320 --> 00:01:02.880
Welcome to Talk Python To Me, a weekly podcast on Python, the language, the libraries, the ecosystem, and the personalities.

00:01:02.880 --> 00:01:04.680
This is your host, Michael Kennedy.

00:01:04.680 --> 00:01:06.880
Follow me on Twitter where I'm @mkennedy,

00:01:07.000 --> 00:01:10.600
and keep up with the show and listen to past episodes at talkpython.fm,

00:01:10.600 --> 00:01:13.680
and follow the show on Twitter via at Talk Python.

00:01:13.680 --> 00:01:18.840
This episode is brought to you by 45 Drives and us over at Talk Python Training.

00:01:18.840 --> 00:01:21.000
Please check out what we're offering during those segments.

00:01:21.000 --> 00:01:22.400
It really helps support the show.

00:01:22.400 --> 00:01:23.440
Hey, Samuel.

00:01:23.440 --> 00:01:25.860
Hi, Mike. Great to meet you. Very excited to be here.

00:01:25.860 --> 00:01:27.440
Yeah, I'm really excited to have you here.

00:01:27.440 --> 00:01:31.980
You've been working on one of my favorite projects these days that is just super, super neat,

00:01:31.980 --> 00:01:36.260
solving a lot of problems, Pydantic, and I'm really excited to talk to you about it.

00:01:36.260 --> 00:01:37.860
Yeah, as I say, it's great to be here.

00:01:37.860 --> 00:01:41.640
I've been doing Pydantic on and off now since 2017,

00:01:41.640 --> 00:01:46.760
but I guess it was when Sebastian Ramirez started using it on FastAPI a couple of years ago.

00:01:46.760 --> 00:01:49.880
A couple of years ago, I don't know when, that it really went crazy.

00:01:49.880 --> 00:01:53.400
And so now it's a lot of work, but it's exciting to see something this popular.

00:01:53.400 --> 00:01:58.240
I'm sure it's a lot of work, but it really seems to have caught the imagination of people

00:01:58.240 --> 00:01:59.800
and really excited about it.

00:01:59.800 --> 00:02:01.240
So fantastic work.

00:02:01.240 --> 00:02:05.980
Before we get into the details there, let's start with just your background.

00:02:06.120 --> 00:02:07.080
How did you get into programming Python?

00:02:07.080 --> 00:02:11.680
I did a bit of programming at university, a lot of, a bit of MATLAB and a bit of C++.

00:02:11.680 --> 00:02:17.900
And then my first job after university, I worked on oil rigs in Indonesia, of all strange things.

00:02:17.900 --> 00:02:22.100
There's a lot of time on an oil rig when you're flat out, and there's a lot of other time when

00:02:22.100 --> 00:02:24.500
you're doing absolutely nothing, don't have much to do.

00:02:24.500 --> 00:02:26.800
And so I programmed quite a lot in that time.

00:02:26.800 --> 00:02:29.900
And I suppose that was when I really got into loving it rather than just doing it when I had to.

00:02:29.900 --> 00:02:33.360
And then I guess Python from there, a bit of JavaScript, a bit of Python ever since.

00:02:33.360 --> 00:02:34.120
Yeah, really cool.

00:02:34.120 --> 00:02:39.380
I think things like MATLAB often do sort of pull people in and they have to learn a little bit

00:02:39.380 --> 00:02:43.500
of programming because it's a pain to just keep typing it into whatever the equivalent of the

00:02:43.500 --> 00:02:45.260
REPL is that MATLAB has.

00:02:45.260 --> 00:02:49.120
You do the .imp file and you kind of get going and you start to combine them.

00:02:49.120 --> 00:02:52.960
And then all of a sudden, it sort of sucks you into the programming world when you maybe

00:02:52.960 --> 00:02:54.020
didn't plan to go that way.

00:02:54.020 --> 00:02:58.100
I worry maybe other people learn it the right way and sit down and read a book and understand

00:02:58.100 --> 00:02:58.760
how to do stuff.

00:02:58.760 --> 00:03:02.660
But there's a lot of things that I wish I had known back then that I learned only through

00:03:02.660 --> 00:03:06.540
reading other code or through banging my head against the wall that would have been really

00:03:06.540 --> 00:03:08.500
easy to learn if someone had showed me them.

00:03:08.500 --> 00:03:09.420
But hey, we got here.

00:03:09.420 --> 00:03:13.860
Yeah, I think many, I'd probably say most people got into programming that way.

00:03:13.860 --> 00:03:14.900
And I think it's all fine.

00:03:14.900 --> 00:03:15.800
Yeah, it's all good.

00:03:15.800 --> 00:03:18.900
So what was living on an oil rig like?

00:03:18.900 --> 00:03:19.900
That must have been insane.

00:03:19.900 --> 00:03:21.060
It was pretty peculiar.

00:03:21.060 --> 00:03:24.340
So half the time I was on land rigs and half the time I was offshore.

00:03:24.340 --> 00:03:26.080
Offshore, the conditions were a lot better.

00:03:26.080 --> 00:03:28.560
Land rigs, the food was pretty horrific.

00:03:28.560 --> 00:03:33.280
And a lot of they were all, I always did like a 14 hour night shift and then occasionally

00:03:33.280 --> 00:03:35.300
had to do the day shift as well and then go on to the night shift.

00:03:35.300 --> 00:03:38.140
So sleep was a problem and the heat was a problem.

00:03:38.140 --> 00:03:43.060
Yeah, it was hard baptism of the real working world after university, I must say.

00:03:43.060 --> 00:03:43.940
Yeah, I guess so.

00:03:43.940 --> 00:03:46.080
It sounds really interesting.

00:03:46.080 --> 00:03:49.820
Not an interesting like, wow, I really want to necessarily go out there and do it.

00:03:49.820 --> 00:03:54.400
But you just must have come away with some like some wild stories and different perspective.

00:03:54.400 --> 00:03:56.980
Yeah, lots of stories that I won't retell now.

00:03:56.980 --> 00:03:59.860
I don't think that's the kind of subject matter you want on your podcast.

00:03:59.860 --> 00:04:03.280
But yeah, I mean, how oil rigs work, well, whatever you think about it and however much

00:04:03.280 --> 00:04:06.100
we all want to get away from them is crazy what you can do.

00:04:06.100 --> 00:04:10.220
And so being like up against the at the coalface of that was really fascinating.

00:04:10.220 --> 00:04:12.520
It's kind of like aerospace, but no one minds crashing.

00:04:12.520 --> 00:04:14.160
So you can innovate.

00:04:14.160 --> 00:04:15.300
You can try a new thing.

00:04:15.300 --> 00:04:17.180
You just the faster you can drill, the better.

00:04:17.320 --> 00:04:18.380
It's all that anyone cares about.

00:04:18.380 --> 00:04:18.620
Yeah.

00:04:18.620 --> 00:04:20.040
Well, most people what they care about.

00:04:20.040 --> 00:04:21.620
Yeah, it must have been a cool adventure.

00:04:21.620 --> 00:04:22.020
Awesome.

00:04:22.020 --> 00:04:27.860
So I thought it'd be fun to start our conversation, not exactly about Pydantic, but just about this

00:04:27.860 --> 00:04:33.100
larger story that larger space that Pydantic lives in.

00:04:33.100 --> 00:04:38.020
But maybe to set the stage, give us like a real quick overview of what problem does Pydantic

00:04:38.020 --> 00:04:38.840
solve for the world?

00:04:38.840 --> 00:04:40.460
What is it and what does it solve for the world?

00:04:40.460 --> 00:04:44.680
So the most simplest way of thinking about it is you've got some user somewhere.

00:04:44.680 --> 00:04:46.740
They might be on the other end of a web connection.

00:04:46.740 --> 00:04:48.440
They might be using a GUI.

00:04:48.440 --> 00:04:50.300
They might be using a CLI.

00:04:50.300 --> 00:04:51.120
It doesn't really matter.

00:04:51.120 --> 00:04:52.640
And they're entering some data.

00:04:52.640 --> 00:04:57.560
And you need to trust the data that you that they put in is correct.

00:04:57.560 --> 00:05:00.580
So with Pydantic, I don't really care what someone enters.

00:05:00.580 --> 00:05:04.200
All I care about is that you can validate it to get what you want.

00:05:04.280 --> 00:05:10.000
So if I need an integer and a string and a list of bytes, all I care about is that I can

00:05:10.000 --> 00:05:12.320
get to that integer, string, and list of bytes.

00:05:12.320 --> 00:05:16.040
I don't care if someone entered bytes for the string field as long as I can decode it.

00:05:16.040 --> 00:05:18.020
Or if they entered a tuple instead of a list.

00:05:18.020 --> 00:05:23.080
But yeah, the fundamental problem is people, I suppose in theory, like intentionally trying

00:05:23.080 --> 00:05:27.840
to do it wrong, but mostly innocently getting something wrong, trying to coerce data into

00:05:27.840 --> 00:05:29.480
the shape that you want it to be in.

00:05:29.480 --> 00:05:34.220
Yeah, it's super easy to say like, well, I read this value from a file and the value

00:05:34.220 --> 00:05:36.140
in the file is a number.

00:05:36.140 --> 00:05:39.320
It says one, but you read it as strings.

00:05:39.320 --> 00:05:42.840
So the thing you sent over is quote one, not the number one.

00:05:42.840 --> 00:05:45.260
And in programming, computers hate that.

00:05:45.260 --> 00:05:49.140
They don't think those things are the same unless maybe your JavaScript and you do it in

00:05:49.140 --> 00:05:50.420
the right order, maybe.

00:05:50.420 --> 00:05:53.560
But excluding that odd case, right?

00:05:53.560 --> 00:05:56.200
A lot of times it's just crash wrong data format or whatever.

00:05:56.200 --> 00:05:58.500
We expected an integer and gave us a string.

00:05:58.760 --> 00:06:02.640
But, you know, Pydantic just looks at it and says, it could be an integer if you want it

00:06:02.640 --> 00:06:02.920
to be.

00:06:02.920 --> 00:06:04.020
So we'll just do that.

00:06:04.020 --> 00:06:04.280
Yeah.

00:06:04.280 --> 00:06:09.080
And I think it's something that there's been a lot of discussion about on Pydantic's issue

00:06:09.080 --> 00:06:09.340
tracker.

00:06:09.340 --> 00:06:13.440
There's an entire label strict about the discussions about how strict we should be.

00:06:13.440 --> 00:06:17.300
And I definitely, I think it's fair to say Pydantic compared to its, the other libraries

00:06:17.300 --> 00:06:22.360
is more lenient, more keen on if we can try and coerce it into that type, we will.

00:06:22.360 --> 00:06:25.060
That really started for me trying to make it simple and performant.

00:06:25.140 --> 00:06:29.520
And I just called the, I decided if something wasn't in by calling int on it and seeing what

00:06:29.520 --> 00:06:29.860
happened.

00:06:29.860 --> 00:06:35.040
That had some kind of strange side cases because I called list on something to see if it was

00:06:35.040 --> 00:06:35.360
a list.

00:06:35.360 --> 00:06:40.360
And that meant you could put a string of integers, a string into a list of ints field.

00:06:40.360 --> 00:06:44.740
And it would first of all, call list on it, turn it into a list and then call int on each

00:06:44.740 --> 00:06:45.060
member.

00:06:45.060 --> 00:06:46.580
And that was completely crazy.

00:06:46.700 --> 00:06:48.420
So we've got stricter over the years.

00:06:48.420 --> 00:06:52.940
And I think that in the future, Pydantic will have to get a bit stricter in particular stuff

00:06:52.940 --> 00:06:57.020
like coercing from a float to an int quite often doesn't make sense or isn't what people

00:06:57.020 --> 00:06:57.360
want.

00:06:57.360 --> 00:07:01.040
But most of the time, just working is really powerful, really helpful.

00:07:01.040 --> 00:07:03.500
If there's going to be some kind of data loss, right?

00:07:03.500 --> 00:07:06.700
If it's 1.0000, coercing that to one's fine.

00:07:06.700 --> 00:07:09.600
If it's 1.52, maybe not.

00:07:09.600 --> 00:07:13.520
Yeah, but it's difficult because we're in the Python world where Python is pretty lenient.

00:07:13.620 --> 00:07:15.640
So it doesn't mind you adding two floats together.

00:07:15.640 --> 00:07:20.500
If you call the int function on a float, it's fine.

00:07:20.500 --> 00:07:24.740
So it's also trying to be Pythonic at the same time as being strict enough for people, but

00:07:24.740 --> 00:07:29.360
without doing stuff that obviously, without not doing stuff that's obvious, because lots

00:07:29.360 --> 00:07:31.100
of people do just want coercion to work.

00:07:31.100 --> 00:07:31.400
Right.

00:07:31.400 --> 00:07:32.080
We'll get into it.

00:07:32.080 --> 00:07:36.200
There's lots of places to plug in and write your own code and do those kinds of checks if

00:07:36.200 --> 00:07:36.940
you really need to.

00:07:36.940 --> 00:07:37.220
Yeah.

00:07:37.220 --> 00:07:37.640
All right.

00:07:37.640 --> 00:07:42.080
Carlos has a good question out there in the live stream, but I'm going to get to that later

00:07:42.080 --> 00:07:43.420
when we get a little bit more into it.

00:07:43.420 --> 00:07:45.900
First, let's talk about some of the other things.

00:07:45.900 --> 00:07:51.380
So Pythonic is surprisingly popular these days, but there's plenty of people, I'm sure, who

00:07:51.380 --> 00:07:54.440
haven't heard of it, who are hearing about it the first time here.

00:07:54.440 --> 00:07:59.040
And there's other libraries that try to solve these same type of problems, right?

00:07:59.040 --> 00:08:04.560
Problem is I've got data in often a dictionary list mix, right?

00:08:04.560 --> 00:08:08.600
Like a list of dictionaries or a dictionary, which contains lists, which contains like that

00:08:08.600 --> 00:08:11.260
kind of object graph type of thing.

00:08:11.260 --> 00:08:17.040
And I want to turn that into probably a class, probably a Python class of some sort or understand

00:08:17.040 --> 00:08:17.940
it in some way.

00:08:18.300 --> 00:08:23.120
So, you know, we've got some of the really simple ways of doing this where I just have

00:08:23.120 --> 00:08:23.300
those.

00:08:23.300 --> 00:08:26.260
I want to stash that into a binary form, which is pickle.

00:08:26.260 --> 00:08:31.080
Pickle has been bad in certain ways because it can run arbitrary Python code.

00:08:31.080 --> 00:08:31.860
That's not ideal.

00:08:31.860 --> 00:08:37.100
There's another related library called quickle, which is like pickle, but doesn't run arbitrary

00:08:37.100 --> 00:08:37.560
code.

00:08:37.560 --> 00:08:38.340
And that's pretty nice.

00:08:38.340 --> 00:08:42.900
We have data classes that look very much like what you're doing with Pydantic.

00:08:42.900 --> 00:08:48.280
We have Marshmallow, which I hear often being used with like Flask and SQLAlchemy and Marshmallow.

00:08:48.280 --> 00:08:53.400
You may want to just sort of give your perspective on like what the choices are out there and where

00:08:53.400 --> 00:08:55.740
things are learning from other libraries and growing from.

00:08:55.740 --> 00:09:01.740
Yeah, I put serialization as in pickle, JSON, YAML, TOML, all of them into a different category

00:09:01.740 --> 00:09:06.740
message pack as a slightly different thing from taking Python objects and trying to turn

00:09:06.740 --> 00:09:07.360
them into classes.

00:09:07.620 --> 00:09:12.200
So putting them to one side, because I think that's a kind of different problem that Pydantic

00:09:12.200 --> 00:09:14.220
and Marshmallow and people aren't trying to solve exactly.

00:09:14.220 --> 00:09:18.560
Then there's data classes, which are the kind of canonical standard library way of doing this.

00:09:18.560 --> 00:09:20.820
They're great, but they don't provide any validation.

00:09:20.820 --> 00:09:27.460
So you can add a type in that says age is an integer, but data classes don't care.

00:09:27.460 --> 00:09:29.680
Whatever you put in will end up in that field.

00:09:29.680 --> 00:09:34.660
And so that's useful if you have a fully type-inted system where you know already that something's

00:09:34.660 --> 00:09:36.380
an integer before you pass it to a data class.

00:09:36.380 --> 00:09:40.380
But if you're loading it from a foreign source, you often don't have that certainty.

00:09:40.380 --> 00:09:43.560
And so that's where libraries like Pydantic and Marshmallow come in.

00:09:43.560 --> 00:09:47.040
Actually, Pydantic has a wrapper for data classes.

00:09:47.040 --> 00:09:52.320
So you basically import the Pydantic version of data classes instead of normal data classes.

00:09:52.320 --> 00:09:56.300
And from there on, Pydantic will do all the validation, give you back a completely standard

00:09:56.300 --> 00:09:56.820
data class.

00:09:56.980 --> 00:09:58.300
Having done the validation.

00:09:58.300 --> 00:10:04.160
Marshmallow is probably, well, it is undoubtedly the biggest, most obvious competitor to Pydantic.

00:10:04.160 --> 00:10:05.040
And it's great.

00:10:05.040 --> 00:10:07.780
I'm not going to sit here and bad mouth it.

00:10:07.780 --> 00:10:10.540
It's been around for longer and it does a lot of things really well.

00:10:10.540 --> 00:10:16.760
Pydantic has just overtaken a few months ago Marshmallow in terms of popularity, in terms

00:10:16.760 --> 00:10:17.500
of GitHub stars.

00:10:17.740 --> 00:10:19.520
Whether you care about that or not is another matter.

00:10:19.520 --> 00:10:24.660
There's also Atras, which kind of predates data classes and is closer to data classes.

00:10:24.660 --> 00:10:29.680
But the big difference between Pydantic and Marshmallow and most of the other competitors

00:10:29.680 --> 00:10:31.300
is Pydantic uses type hints.

00:10:31.660 --> 00:10:36.120
So that one means you don't have to learn a whole new kind of micro language to define

00:10:36.120 --> 00:10:36.520
types.

00:10:36.520 --> 00:10:39.320
You just write your classes and it works.

00:10:39.320 --> 00:10:43.340
It works with mypy and with your static type analysis.

00:10:43.340 --> 00:10:48.900
It works with your IDE, like, well, Pycharm now, because there's an amazing extension.

00:10:48.900 --> 00:10:52.540
I forgot the name of the guy who wrote it, but there's an amazing extension that I used the

00:10:52.540 --> 00:10:56.140
whole time with Pycharm that means it works seamlessly with Pydantic.

00:10:56.140 --> 00:10:58.020
And there's some exciting stuff happening.

00:10:58.020 --> 00:11:00.020
Microsoft, they emailed me actually two days ago.

00:11:00.480 --> 00:11:06.880
One of their technical fellows about extending their language server or their front-end for

00:11:06.880 --> 00:11:10.400
language server, Pyright, to work with Pydantic and other such libraries.

00:11:10.400 --> 00:11:14.660
So because you're using standard type hints, all the other stuff, including your brain, should,

00:11:14.660 --> 00:11:16.220
in theory, click into place.

00:11:16.220 --> 00:11:17.520
Yeah, that's really neat.

00:11:17.520 --> 00:11:21.760
I do think it makes sense to separate sort of the serialization file, save me a file,

00:11:21.760 --> 00:11:23.320
load a file type of thing out.

00:11:23.320 --> 00:11:28.060
I really love the way that the type hints work in there because you can almost immediately

00:11:28.060 --> 00:11:29.780
understand what's happening.

00:11:29.780 --> 00:11:34.660
It's not like, oh, this is the way in which you describe the schema and the way you describe

00:11:34.660 --> 00:11:35.340
the transformations.

00:11:35.340 --> 00:11:37.000
It's just, here's a class.

00:11:37.000 --> 00:11:38.780
It has some fields.

00:11:38.780 --> 00:11:39.700
Those fields have types.

00:11:39.700 --> 00:11:40.980
That's all you need to know.

00:11:40.980 --> 00:11:42.660
And Pydantic will make the magic happen.

00:11:42.660 --> 00:11:46.780
What would you say the big difference between Pydantic and Marshmallow is?

00:11:46.780 --> 00:11:50.360
And I haven't used Marshmallow that frequently, so I don't know it super well.

00:11:50.560 --> 00:11:52.140
I would first give the same proviso.

00:11:52.140 --> 00:11:53.660
I haven't used it that much either.

00:11:53.660 --> 00:11:58.020
I probably, if I was more disciplined, I'd have sat down and used it for some time before

00:11:58.020 --> 00:11:58.680
building Pydantic.

00:11:58.680 --> 00:12:00.480
But that's not always the way things work.

00:12:00.480 --> 00:12:04.160
The main difference is it doesn't use type hints or it doesn't primarily use type hints

00:12:04.160 --> 00:12:07.180
as its source of data about what type something is.

00:12:07.180 --> 00:12:13.820
Pydantic is around, from my memory, I can check, but significantly more performant than Marshmallow.

00:12:14.100 --> 00:12:17.320
Yeah, you actually have some benchmarks on the site and we can talk about that in a little

00:12:17.320 --> 00:12:18.920
bit and compare those.

00:12:18.920 --> 00:12:19.080
Yeah.

00:12:19.080 --> 00:12:19.340
Yeah.

00:12:19.340 --> 00:12:22.380
So just briefly, it's about two and a half times faster.

00:12:22.380 --> 00:12:29.080
The advantage of Marshmallow at the moment is it has more logic around customizing how

00:12:29.080 --> 00:12:30.740
you serialize types.

00:12:30.740 --> 00:12:35.980
So when you're going back from a class to a dictionary or list of dictionaries and then

00:12:35.980 --> 00:12:42.020
out to JSON or whatever, Marshmallow has some really cool tools there, which Pydantic doesn't

00:12:42.020 --> 00:12:42.360
have yet.

00:12:42.440 --> 00:12:47.020
And I'm hoping to build into V2 some more powerful ways of customizing serialization.

00:12:47.020 --> 00:12:47.400
Okay.

00:12:47.400 --> 00:12:48.180
Fantastic.

00:12:48.180 --> 00:12:53.320
This portion of Talk Python is brought to you by 45 Drives.

00:12:53.320 --> 00:12:59.000
45 Drives offers the only enterprise data storage servers powered by open source.

00:12:59.000 --> 00:13:04.080
They build their solutions with off-the-shelf hardware and use software-defined open source

00:13:04.080 --> 00:13:06.980
designs that are unmatched in price and flexibility.

00:13:06.980 --> 00:13:12.420
The open source solutions 45 Drives uses are powerful, robust, and completely different.

00:13:12.420 --> 00:13:18.600
And best of all, they come with zero software licensing fees and no vendor lock-in.

00:13:18.600 --> 00:13:23.980
45 Drives offers servers ranging from four to 60 bays and can guide your organization through

00:13:23.980 --> 00:13:26.060
any sized data storage challenge.

00:13:26.060 --> 00:13:30.760
Check out what they have to offer over at talkpython.fm/45 Drives.

00:13:31.000 --> 00:13:35.080
If you get in touch with them and say you heard about their offer from us, you'll get a chance

00:13:35.080 --> 00:13:36.400
to win a custom front plate.

00:13:36.400 --> 00:13:41.980
So visit talkpython.fm/45 Drives or just click the link in your podcast player.

00:13:44.880 --> 00:13:46.620
Let's dive into it.

00:13:46.620 --> 00:13:50.380
And I want to talk about some of the core features here.

00:13:50.380 --> 00:13:56.600
Maybe we could start with just you walking us through a simple example of creating a class

00:13:56.600 --> 00:13:59.360
and then taking some data and parsing over.

00:13:59.360 --> 00:14:01.340
And you've got this nice example right here on the homepage.

00:14:01.340 --> 00:14:03.640
I think this is so good to just sort of look.

00:14:03.640 --> 00:14:08.560
There's a bunch of little nuances to cool things that happen here that I think people will benefit from.

00:14:08.560 --> 00:14:11.920
Yeah, so you're obviously defining your class user here.

00:14:11.920 --> 00:14:16.560
Very simple inheritance from base model, no decorator.

00:14:16.560 --> 00:14:20.920
I thought about the beginning that like this should work for people who haven't been writing

00:14:20.920 --> 00:14:24.920
Python for the last 10 years and where decorators look like strange magic.

00:14:24.920 --> 00:14:27.900
I think using inheritance is the obvious way to do it.

00:14:27.900 --> 00:14:30.060
And then obviously we define our fields.

00:14:30.060 --> 00:14:36.500
The key thing really is that the type int in the case of ID is used to define what type that

00:14:36.500 --> 00:14:37.280
field is going to be.

00:14:37.720 --> 00:14:42.340
And then if we do give it a value as we do with name, that means that the field is not required.

00:14:42.340 --> 00:14:43.340
It has a default value.

00:14:43.340 --> 00:14:46.700
And obviously we can infer the type there from the default, which is a string.

00:14:46.700 --> 00:14:50.900
Then sign up timestamp is obviously an optional date time.

00:14:50.900 --> 00:14:51.860
So it can be none.

00:14:51.860 --> 00:14:55.760
And critically here, you could either enter none or leave it blank.

00:14:55.760 --> 00:14:58.360
And it would again be none.

00:14:58.360 --> 00:15:00.920
And then we have friends, which is a more complex type.

00:15:00.920 --> 00:15:02.040
That's a list of integers.

00:15:02.040 --> 00:15:06.980
And the cool stuff is because we're just using Python type int, we can burrow down into lists

00:15:06.980 --> 00:15:10.460
of dicks, of lists, of sets, of whatever you like within reason.

00:15:10.460 --> 00:15:12.040
And it will all continue to work.

00:15:12.040 --> 00:15:15.960
And then looking at the external data, again, we see a few things like we were talking about

00:15:15.960 --> 00:15:16.460
the coercion.

00:15:16.460 --> 00:15:16.660
Right.

00:15:16.660 --> 00:15:21.040
This external data is just a dictionary that you probably have gotten from an API call,

00:15:21.040 --> 00:15:22.140
but it could have come from anywhere.

00:15:22.140 --> 00:15:23.640
It doesn't have to come from there.

00:15:23.700 --> 00:15:23.900
Right.

00:15:23.900 --> 00:15:24.380
Exactly.

00:15:24.380 --> 00:15:25.400
Anywhere outside.

00:15:25.400 --> 00:15:27.720
But right now we've got it as far as being a dictionary.

00:15:27.720 --> 00:15:31.000
So the point here is we're doing a bit of coercion.

00:15:31.000 --> 00:15:39.460
So trivial converting from a string 123 to the number 123, but then a bit more complex,

00:15:39.460 --> 00:15:43.200
parsing the date and converting that into a date object.

00:15:43.320 --> 00:15:43.500
Right.

00:15:43.500 --> 00:15:50.600
So you have the, in here, the data that's passed in, you've got a quote, 2019-06-01 and

00:15:50.600 --> 00:15:51.080
a time.

00:15:51.080 --> 00:15:56.380
And this is notoriously tricky because things like JSON don't even support dates.

00:15:56.380 --> 00:15:58.300
Like, they'll freak out if you try to send one over.

00:15:58.540 --> 00:16:01.840
So you just got this string, but it'll be turned into a date.

00:16:01.840 --> 00:16:02.180
Yeah.

00:16:02.180 --> 00:16:06.540
And we do a whole bunch of different things to try and do all of the sensible date formats.

00:16:06.540 --> 00:16:11.580
There's obviously a limit as how far to go, because one of the things Pedantic can do is

00:16:11.580 --> 00:16:15.760
it can interpret integers as dates using Unix timestamps.

00:16:15.760 --> 00:16:20.400
And if they're over some threshold in about two centuries from now, it assumes there are

00:16:20.400 --> 00:16:20.820
milliseconds.

00:16:20.820 --> 00:16:26.860
So it works with Unix milliseconds, which are often used, but it does also lead to confusions

00:16:26.860 --> 00:16:31.000
when someone puts in one, two, three as a date and it's three seconds after 1970.

00:16:31.000 --> 00:16:35.740
There are like, there's an ongoing debate about exactly like what you should try and coerce

00:16:35.740 --> 00:16:36.600
and when it gets magic.

00:16:36.600 --> 00:16:41.820
But for me, it's, there's a number of times I've just found it incredibly useful to, that

00:16:41.820 --> 00:16:42.600
it just works.

00:16:42.600 --> 00:16:49.080
So for example, the string format that Postgre uses when you use to JSON just works with Pedantic.

00:16:49.080 --> 00:16:52.340
So you don't even have to think about whether that's come through as a date or as a string

00:16:52.340 --> 00:16:54.140
until you're worried about a limit of performance.

00:16:54.140 --> 00:16:55.880
Most of that stuff just works.

00:16:56.140 --> 00:16:56.220
Yeah.

00:16:56.220 --> 00:17:01.180
Then one of the things that I think is super interesting is you have these friend IDs that

00:17:01.180 --> 00:17:02.000
you're passing over.

00:17:02.000 --> 00:17:05.360
And you said in Pedantic, it gets a list of integers.

00:17:05.360 --> 00:17:11.300
And in the external data, it is a list of sometimes integers and sometimes strings.

00:17:11.300 --> 00:17:17.140
But once it gets parsed across, it not just looks at the immediate fields, but it looks at,

00:17:17.140 --> 00:17:20.500
say, things inside of a list and says, oh, you wanted a list of integers here.

00:17:20.500 --> 00:17:23.500
This, this list, it has a string in it, but it's like quote three.

00:17:23.620 --> 00:17:24.120
So it's fine.

00:17:24.120 --> 00:17:24.460
Yeah.

00:17:24.460 --> 00:17:29.460
And this is where, where it gets cool because we can go recursively down into the rabbit hole

00:17:29.460 --> 00:17:30.940
and it will continue to validate.

00:17:31.160 --> 00:17:38.040
One of the tricky things that I think is what most people want, but where our language fails us is about the word validation.

00:17:38.040 --> 00:17:43.380
Because quite often validation sounds like I'm checking the input data is in the form that I said.

00:17:43.800 --> 00:17:45.740
That's kind of not what Pedantic is doing.

00:17:45.740 --> 00:17:47.440
It's optimistically trying to parse.

00:17:47.440 --> 00:17:52.200
It doesn't care what the list contains in a sense, as long as it can find a way to make that into an int.

00:17:52.200 --> 00:17:58.660
So this wouldn't be a good library to use for, for like unit testing and checking that something is the way that it should be,

00:17:58.660 --> 00:18:01.120
because it's going to accept a million different things.

00:18:01.120 --> 00:18:03.920
It's going to be as lenient as possible in what it will take in.

00:18:04.180 --> 00:18:05.560
But that's by design.

00:18:05.560 --> 00:18:05.780
Yeah.

00:18:05.780 --> 00:18:15.860
The way to take this external dictionary and then get Pedantic to parse it is you just pass it as keyword arguments to the class constructor.

00:18:16.100 --> 00:18:20.380
So you say user of star star dictionary.

00:18:20.380 --> 00:18:23.220
So that'll, you know, explode that out to keyword arguments.

00:18:23.220 --> 00:18:24.080
And that's it.

00:18:24.080 --> 00:18:26.340
That runs the entire parsing, right?

00:18:26.340 --> 00:18:27.340
That's super simple.

00:18:27.340 --> 00:18:27.680
Yeah.

00:18:27.680 --> 00:18:30.720
And that's, again, by design to make it just the simplest way of doing it.

00:18:30.720 --> 00:18:35.320
If you want to do crazy complex stuff like constructing models without doing validation,

00:18:35.320 --> 00:18:38.360
because you know it's already been validated, that's all possible.

00:18:38.360 --> 00:18:44.580
But the simplest interface, just calling in it on the class is designed to work and do the validation.

00:18:44.580 --> 00:18:45.140
Yeah, cool.

00:18:45.580 --> 00:18:51.240
One thing I think is really neat and not obvious right away is that you can nest these things as well, right?

00:18:51.240 --> 00:18:58.740
Like I could have a shopping cart and the shopping cart could have a list of orders and each order in that list could be a Pedantic model itself, right?

00:18:58.740 --> 00:18:59.180
Exactly.

00:18:59.180 --> 00:19:03.100
And it's probably an open question as to how complex we should make this first example.

00:19:03.100 --> 00:19:04.760
Maybe it's already too complicated.

00:19:04.760 --> 00:19:06.780
Maybe it doesn't demonstrate all the power.

00:19:06.780 --> 00:19:08.940
But yeah, I think it's probably about right.

00:19:08.940 --> 00:19:10.880
But yeah, you can go recursive.

00:19:10.880 --> 00:19:14.940
You can even do some crazy things like the root type of a model can actually not itself

00:19:15.060 --> 00:19:16.920
be a sequence of fields.

00:19:16.920 --> 00:19:17.980
It can be a list itself.

00:19:17.980 --> 00:19:21.240
So there is a long tail of complex stuff you can do.

00:19:21.240 --> 00:19:21.980
But you're right.

00:19:21.980 --> 00:19:26.540
The inheritance of different models is a really powerful way of defining types.

00:19:26.540 --> 00:19:31.300
Because in reality, our models are never a nice key in value of different types.

00:19:31.300 --> 00:19:34.340
They always have complex items deeper down.

00:19:34.340 --> 00:19:34.680
Right.

00:19:34.680 --> 00:19:35.580
That's really cool.

00:19:35.580 --> 00:19:38.900
And on top of these, I guess we can add validation.

00:19:38.900 --> 00:19:41.880
Like you have the, you said, the very optimistic validation.

00:19:41.880 --> 00:19:46.400
Like if in the friends list, it said one, two, comma, Jane.

00:19:46.400 --> 00:19:51.560
Well, it's probably going to crash when it tries to convert Jane into an integer and say, no, no, no,

00:19:51.560 --> 00:19:52.200
this is wrong.

00:19:52.200 --> 00:19:53.760
And it gives you a really good error message.

00:19:53.760 --> 00:19:57.360
It says something like the third item in this list is Jane.

00:19:57.420 --> 00:19:58.660
And that's not an integer, right?

00:19:58.660 --> 00:20:00.400
It doesn't just go, well, data is bad.

00:20:00.400 --> 00:20:02.340
You know, too bad for you.

00:20:02.340 --> 00:20:02.580
Yeah.

00:20:02.580 --> 00:20:05.200
But you could decide, perhaps Jane's a strange case.

00:20:05.200 --> 00:20:10.620
But if you wanted the word one, O-N-E, to somehow work, you could add your own logic relatively

00:20:10.620 --> 00:20:13.640
trivially via a decorator, which said, okay, cool.

00:20:13.640 --> 00:20:18.680
If we get the word one or the word two or the word three, then we can convert them to the equivalent

00:20:18.680 --> 00:20:19.400
integers.

00:20:19.400 --> 00:20:21.680
And that's relatively easy to add.

00:20:21.680 --> 00:20:21.940
Yeah.

00:20:21.940 --> 00:20:22.260
Cool.

00:20:22.260 --> 00:20:27.160
Now, one thing that I guess the scenario where people run into this often run into working

00:20:27.160 --> 00:20:32.380
with Pydantic is it's sort of the exchange layer for FastAPI, which is one of the more

00:20:32.380 --> 00:20:34.200
popular API frameworks these days.

00:20:34.200 --> 00:20:40.600
And you just say your API function takes some Pydantic model and it returns some Pydantic model

00:20:40.600 --> 00:20:42.380
and, you know, magic happens.

00:20:42.380 --> 00:20:47.140
But if you're not working in that world, if you're not in FastAPI and most people doing

00:20:47.140 --> 00:20:51.780
web development these days are not because, you know, it's the most popular framework and

00:20:51.780 --> 00:20:54.620
it's also certainly not the most popular legacy framework.

00:20:54.620 --> 00:21:01.820
So you can do things like create these models directly in your code, let it do the validation

00:21:01.820 --> 00:21:02.120
there.

00:21:02.120 --> 00:21:07.260
And then if you need to return the whole object graph as a dictionary, you can just say model.dict,

00:21:07.260 --> 00:21:07.540
right?

00:21:07.540 --> 00:21:07.820
Yeah.

00:21:08.120 --> 00:21:11.100
And also model.json and that will take it right out to JSON.

00:21:11.100 --> 00:21:11.760
As a string.

00:21:11.760 --> 00:21:12.220
Yeah.

00:21:12.220 --> 00:21:13.940
So it will turn a string of that JSON.

00:21:13.940 --> 00:21:14.480
Yeah.

00:21:14.480 --> 00:21:18.460
And you can even swap in more performant JSON encoders and decoders.

00:21:18.460 --> 00:21:22.780
And this is why I was talking about like there's already some power in what you can do here and

00:21:22.780 --> 00:21:24.360
which fields you can exclude.

00:21:24.900 --> 00:21:25.900
And it's pretty powerful.

00:21:25.900 --> 00:21:32.240
But like, as I say, something in V2 that we might look at to make even more powerful is customizing

00:21:32.240 --> 00:21:32.940
how you do this.

00:21:32.940 --> 00:21:33.260
Nice.

00:21:33.260 --> 00:21:38.020
You might expect to just call .dict and it converts it to a dictionary and reverses it,

00:21:38.020 --> 00:21:38.440
if you will.

00:21:38.440 --> 00:21:43.860
But there's actually a lot of parameters and arguments you can pass in to have more control.

00:21:43.860 --> 00:21:46.960
Do you want to talk about maybe just some of the use cases there?

00:21:47.120 --> 00:21:47.300
Yeah.

00:21:47.300 --> 00:21:52.040
As we see here, you can choose to include specific fields and the rest will be excluded

00:21:52.040 --> 00:21:52.660
by default.

00:21:52.660 --> 00:21:57.940
You can say exclude, which will exclude certain fields and the rest will be included by default.

00:21:57.940 --> 00:22:03.700
Those include and exclude fields can do some pretty ridiculously crazy logic going recursively

00:22:03.700 --> 00:22:10.020
into the models that you're looking at and excluding specific fields from those models or specific

00:22:10.020 --> 00:22:12.220
items from lists, even some of the code.

00:22:12.220 --> 00:22:14.880
I forget who wrote that, but like I wrote the first version of it.

00:22:14.880 --> 00:22:16.620
I wrote the second or third version of it.

00:22:16.620 --> 00:22:19.240
And I was looking through about the 10th version of it the other day.

00:22:19.240 --> 00:22:22.440
And it is some of the most complex bits of Pydantic.

00:22:22.440 --> 00:22:24.060
But that's amazingly powerful.

00:22:24.060 --> 00:22:28.920
And then we didn't talk about aliases, but you can imagine if you were interacting with the

00:22:28.920 --> 00:22:33.680
JavaScript framework, you might be using camel case on the front end for like user names,

00:22:33.680 --> 00:22:39.420
name with a capital N, but have user underscore name in Python world where we're using underscores.

00:22:39.420 --> 00:22:42.700
We can manage that by setting aliases on each field.

00:22:42.700 --> 00:22:47.520
So saying that the user underscore name field has the alias user underscore N name.

00:22:47.520 --> 00:22:52.000
And then we can obviously export to a dictionary using those aliases if we wish.

00:22:52.000 --> 00:22:52.760
Oh, that's cool.

00:22:52.760 --> 00:23:00.120
So you can program Pydonic style classes, even if you're consuming, say, a Java or C#

00:23:00.120 --> 00:23:04.420
API that clearly uses a different format or style for its naming.

00:23:04.420 --> 00:23:04.880
Exactly.

00:23:04.880 --> 00:23:12.500
And you can then export again to back to those aliases when you want to go on to like pipe

00:23:12.500 --> 00:23:15.640
it on down your data processing flow or however you're going to do it.

00:23:15.640 --> 00:23:19.020
And in fact, one of the things that will come up in future will be different aliases for import

00:23:19.020 --> 00:23:21.400
on the inside way and the out way.

00:23:21.400 --> 00:23:24.820
But until so far, there's just one, but that's powerful.

00:23:24.820 --> 00:23:28.120
And then you can exclude fields that have defaults.

00:23:28.120 --> 00:23:32.080
So if you're trying to save that data to a database and you don't want to save more than

00:23:32.080 --> 00:23:35.440
you need to, you can exclude stuff where it has the default value.

00:23:35.680 --> 00:23:40.000
And you can exclude fields which are none, which again is often the same thing, but there

00:23:40.000 --> 00:23:42.200
are subtle cases where those are different requirements.

00:23:42.200 --> 00:23:42.520
Yeah.

00:23:42.520 --> 00:23:46.540
And that also might really just, even if you're not saving it to a database, you know, that

00:23:46.540 --> 00:23:51.920
will lower the size of the JSON traffic between say microservices or something like that.

00:23:51.920 --> 00:23:55.400
If you don't need to send the defaults over, especially the nones, because they're probably

00:23:55.400 --> 00:24:00.700
going to go to the thing they get and go, give me the value or none in whatever language

00:24:00.700 --> 00:24:01.640
they're using, right?

00:24:01.640 --> 00:24:02.500
Something to that effect.

00:24:02.500 --> 00:24:03.500
It'll mean the same.

00:24:03.500 --> 00:24:04.040
Yeah.

00:24:04.040 --> 00:24:05.820
So we have complex stuff here.

00:24:05.820 --> 00:24:09.160
So we have exclude unset and exclude defaults.

00:24:09.160 --> 00:24:14.540
So you can decide exactly how you want to exclude it to get just the fields that are actually live,

00:24:14.540 --> 00:24:15.960
as it were, that have custom values.

00:24:15.960 --> 00:24:16.260
Yeah.

00:24:16.260 --> 00:24:20.600
So you have this dict thing, which will turn a dictionary and then you have the JSON one,

00:24:20.600 --> 00:24:22.200
which is interesting.

00:24:22.200 --> 00:24:23.280
We talked about that one.

00:24:23.280 --> 00:24:24.940
It also, you also have copy.

00:24:24.940 --> 00:24:29.460
So you, is that like a shallow copy if you want to clone it off or something?

00:24:29.460 --> 00:24:32.920
It can be a deep copy, but you can update some fields on the way through.

00:24:33.360 --> 00:24:36.760
So there are multiple different contexts where you might want to do that, where you want

00:24:36.760 --> 00:24:40.720
different models where you can go and edit one of them and not damage the other one,

00:24:40.720 --> 00:24:43.480
or you want to modify a model as you're copying it.

00:24:43.480 --> 00:24:49.160
We also have a setting on config that prevents you, that makes fields pseudo immutable.

00:24:49.600 --> 00:24:52.000
So it doesn't allow you to change that value.

00:24:52.000 --> 00:24:55.440
It doesn't stop you changing stuff within that value because there's no way of doing that in Python.

00:24:55.440 --> 00:24:58.920
But that's another case where you might want to use copy because you've set your model up to be

00:24:58.920 --> 00:25:01.960
effectively static and you want to create a new one.

00:25:01.960 --> 00:25:05.880
If you were being super careful and strict, you would say, I'm never going to modify my model.

00:25:05.880 --> 00:25:07.480
I'm just going to copy it when I want a new one.

00:25:07.480 --> 00:25:07.760
Yeah.

00:25:07.760 --> 00:25:08.360
Perfect.

00:25:08.360 --> 00:25:08.620
Yeah.

00:25:08.620 --> 00:25:11.780
Certain things you could enforce that they don't change like strings and numbers.

00:25:11.780 --> 00:25:15.260
But if it's a list, like the list can't point somewhere else, but what's in the list,

00:25:15.260 --> 00:25:16.920
you can't really do anything about that, right?

00:25:16.920 --> 00:25:17.140
Yeah.

00:25:17.140 --> 00:25:21.280
So another thing that I think is worth touching on that's interesting is there's some obvious

00:25:21.280 --> 00:25:26.420
things that are the types that can be set for the field types.

00:25:26.420 --> 00:25:31.160
And then the data exchange conversion types, bools, integers, floats, strings, and so on.

00:25:31.160 --> 00:25:36.500
But then it gets further, it gets more specialized down there as you go.

00:25:36.500 --> 00:25:42.360
So for example, you can have unions, you can have frozen sets, you can have iterables,

00:25:42.360 --> 00:25:46.240
callables, network addresses, all kinds of stuff, right?

00:25:46.240 --> 00:25:46.500
Yeah.

00:25:46.500 --> 00:25:50.220
We try and support almost anything you can think of from the Python standard library.

00:25:50.220 --> 00:25:54.620
And then if you go on down, we get to a whole bunch of things that aren't supported

00:25:54.620 --> 00:25:59.440
or don't have an obvious equivalent within the standard library, but where you go on

00:25:59.440 --> 00:26:02.460
down to Pydantic types on the right.

00:26:02.460 --> 00:26:07.740
So where you're looking is in the example, but Pydantic types, then we get into things

00:26:07.740 --> 00:26:13.360
that don't exist within, or there isn't an obvious type for in Pydantic, in Python.

00:26:13.360 --> 00:26:19.360
There's a kind of subtle point here that like often these types are used to enforce more

00:26:19.360 --> 00:26:19.800
validation.

00:26:19.800 --> 00:26:23.100
So what's returned is an existing Python type.

00:26:23.180 --> 00:26:28.220
So here we have file path, which returns a path, but it just guarantees that that file

00:26:28.220 --> 00:26:28.600
exists.

00:26:28.600 --> 00:26:32.760
And directory path, similarly, is just a path, but it will confirm that it is a directory.

00:26:32.760 --> 00:26:37.880
And email string just returns you a string, but it's validated that it's a legitimate email

00:26:37.880 --> 00:26:38.240
address.

00:26:38.240 --> 00:26:40.520
But then some do more complex stuff.

00:26:40.520 --> 00:26:45.880
So name email will return you, allow you to split out names and email addresses and more

00:26:45.880 --> 00:26:46.920
complex things.

00:26:46.920 --> 00:26:51.240
Got credit cards, colors, URLs, all sorts of good stuff there.

00:26:51.320 --> 00:26:51.440
Yeah.

00:26:51.440 --> 00:26:51.800
Yes.

00:26:51.800 --> 00:26:57.420
So there's a Mexican bank called, excuse my pronunciation, Cavenka, who use the credit card

00:26:57.420 --> 00:27:00.240
fields for all of their validation of credit card numbers.

00:27:00.240 --> 00:27:00.480
Yeah.

00:27:00.480 --> 00:27:01.380
It's super interesting.

00:27:01.380 --> 00:27:03.320
And you think about how do you do this validation?

00:27:03.320 --> 00:27:04.860
How do you do these conversions yourself?

00:27:04.860 --> 00:27:09.420
And not only do you have to figure that out, but then you've got to do it in the context of

00:27:09.420 --> 00:27:10.820
like a larger data conversion.

00:27:10.820 --> 00:27:16.180
And here you just say, this field is a type email and it either is an email or it's going

00:27:16.180 --> 00:27:17.180
to tell you that it's invalid.

00:27:17.180 --> 00:27:17.440
Right.

00:27:17.440 --> 00:27:17.760
Yeah.

00:27:17.760 --> 00:27:18.540
I like this a lot.

00:27:18.540 --> 00:27:22.460
You know, one thing that we've been talking about that's pretty straightforward is using

00:27:22.460 --> 00:27:24.220
this for APIs, right?

00:27:24.220 --> 00:27:29.020
Like somebody is doing a JSON post and you get that as a dictionary in your web framework,

00:27:29.020 --> 00:27:31.300
and then you kind of validate it and convert it and so on.

00:27:31.300 --> 00:27:37.460
But it seems like you could even use this for like web forms and other type of things,

00:27:37.460 --> 00:27:37.720
right?

00:27:37.720 --> 00:27:42.880
Somebody's submitting some kind of ATP post of a form and it comes over if you want to say

00:27:42.880 --> 00:27:45.880
these values are required, this one's an email and so on.

00:27:45.880 --> 00:27:46.140
Yeah.

00:27:46.140 --> 00:27:51.260
So all of the form validation for multiple different projects I've built, some open source, lots

00:27:51.260 --> 00:27:56.860
proprietary use Pydantic for form validation and use the error messages directly back to the

00:27:56.860 --> 00:27:57.100
user.

00:27:57.480 --> 00:28:03.840
I've got quite a lot of JavaScript that basically works with Pydantic to build forms and do validation

00:28:03.840 --> 00:28:05.380
and return that validation to the user.

00:28:05.380 --> 00:28:10.320
I've never quite gone far enough to really open source that and push it as a React plugin

00:28:10.320 --> 00:28:12.500
or something, but it wouldn't be so hard to do.

00:28:12.500 --> 00:28:13.540
So let me ask you this.

00:28:13.540 --> 00:28:16.580
And maybe when you say React, maybe that's enough of an answer already.

00:28:16.580 --> 00:28:22.880
But if it's a server side processing form and it's not a single page sort of front and framework

00:28:22.880 --> 00:28:28.500
style of form submission, often what you need to do is return back to them the wrong data,

00:28:28.500 --> 00:28:29.100
right?

00:28:29.100 --> 00:28:33.500
If they type in something that's not an email and over there it's not a number, you still

00:28:33.500 --> 00:28:38.080
want to leave the form filled with that bad email and that thing that's not a number.

00:28:38.080 --> 00:28:39.780
So you can say, those are wrong.

00:28:39.780 --> 00:28:41.020
Keep on typing them.

00:28:41.020 --> 00:28:45.400
Is there a way to make that kind of round tripping work with Pydantic?

00:28:45.720 --> 00:28:47.480
I've tried and haven't been able to do it.

00:28:47.480 --> 00:28:52.920
But if it's a front end framework, it's easy to say, submit this form, catch the error and

00:28:52.920 --> 00:28:56.820
show the error because you're not actually leaving the page or clearing the form with a

00:28:56.820 --> 00:28:57.140
reload.

00:28:57.140 --> 00:28:58.540
Yeah, the short answer is no.

00:28:58.540 --> 00:29:03.300
And it's something there's an issue about for V2 to return like the wrong value always

00:29:03.300 --> 00:29:04.660
in the context of each error.

00:29:04.660 --> 00:29:07.520
Often you kind of need it to make the error make sense.

00:29:07.520 --> 00:29:11.180
But at the moment, it's generally not available and we will add it in V2.

00:29:11.400 --> 00:29:15.320
What I've done in React is obviously you've still got the values that went into on the

00:29:15.320 --> 00:29:15.560
form.

00:29:15.560 --> 00:29:18.720
So you don't clear any of the inputs.

00:29:18.720 --> 00:29:23.500
You just add the errors to those fields and set the whatever it is, invalid header.

00:29:23.500 --> 00:29:24.360
So they're nice and red.

00:29:24.360 --> 00:29:24.960
Yeah, exactly.

00:29:24.960 --> 00:29:29.300
So if it was a front end framework like React or Vue, I can see this working perfectly as

00:29:29.300 --> 00:29:30.700
you try to submit it for a form.

00:29:30.700 --> 00:29:35.880
But if you got to round trip it with like a Flask, a Pyramid or Django form or something

00:29:35.880 --> 00:29:39.260
that doesn't have it, then the fact that it doesn't capture the data.

00:29:39.260 --> 00:29:42.620
I mean, I guess you could return back just the original posted dictionary, but yeah.

00:29:42.620 --> 00:29:42.820
Yeah.

00:29:42.820 --> 00:29:43.860
So you have the original.

00:29:43.860 --> 00:29:44.200
Yeah.

00:29:44.200 --> 00:29:46.320
Like you say, you've normally normally there.

00:29:46.320 --> 00:29:50.760
It's not too many different levels and it's relatively simple to combine up the dictionary

00:29:50.760 --> 00:29:53.420
you got straight off form submission with the errors.

00:29:53.420 --> 00:29:54.920
But yeah, it's you're right.

00:29:54.920 --> 00:29:55.840
It's something we should improve.

00:29:55.840 --> 00:29:56.100
Yeah.

00:29:56.100 --> 00:30:01.800
I'm not sure that you necessarily need to improve it because it it's mission is being fulfilled

00:30:01.800 --> 00:30:03.260
exactly how it is.

00:30:03.260 --> 00:30:06.660
And if it has this time, but well, sometimes it throws errors and sometimes it doesn't.

00:30:06.660 --> 00:30:10.920
And then like just report, I don't know, it seems like you could overly complicate it as

00:30:10.920 --> 00:30:11.100
well.

00:30:11.100 --> 00:30:15.880
I think that there's like a really difficult challenge in tragedy of the commons in like

00:30:15.880 --> 00:30:17.660
someone wants a niche feature.

00:30:17.660 --> 00:30:19.300
Someone else wants that feature.

00:30:19.300 --> 00:30:20.800
You get 10 people wanting that feature.

00:30:20.800 --> 00:30:23.460
You feel under overwhelming pressure to implement that feature.

00:30:23.460 --> 00:30:27.940
But you forget that there's, I forget, you know, there's like 6,000 people who started,

00:30:27.940 --> 00:30:31.340
but it's like 10, 12,000 projects that use Pydantic.

00:30:31.340 --> 00:30:33.020
Those people haven't asked for that.

00:30:33.020 --> 00:30:38.380
Do they want it or would they actively prefer that Pydantic was simpler, faster, smaller?

00:30:38.380 --> 00:30:39.140
Exactly.

00:30:39.140 --> 00:30:40.020
Right.

00:30:40.020 --> 00:30:42.780
Part of the beauty of it is it's so simple, right?

00:30:42.780 --> 00:30:43.080
I do.

00:30:43.080 --> 00:30:43.960
I get the value.

00:30:43.960 --> 00:30:44.960
I define the class.

00:30:44.960 --> 00:30:46.440
I star, star, take the data.

00:30:46.440 --> 00:30:49.320
And it's either good or it's crashed, right?

00:30:49.320 --> 00:30:51.900
If it gets past that line, you should be happy.

00:30:51.900 --> 00:30:52.160
Yeah.

00:30:52.160 --> 00:30:55.980
And I think that like I'm pretty determined to keep that stuff that simple.

00:30:55.980 --> 00:30:59.860
There are those who want to change it, who say initializing the class shouldn't do the

00:30:59.860 --> 00:31:01.700
validation, then you should call a validate method.

00:31:01.700 --> 00:31:03.440
I'm not into that at all.

00:31:03.440 --> 00:31:06.660
There's stuff where I'm definitely going to keep it simple and there's stuff where I'm

00:31:06.660 --> 00:31:07.860
really happy to add more things.

00:31:07.860 --> 00:31:09.700
So we were talking about custom types before.

00:31:09.700 --> 00:31:14.180
I'm really happy to add virtually, not virtually any, but like a lot of custom types when someone

00:31:14.180 --> 00:31:19.000
wants it, because if you don't need it, it just sits there and mostly doesn't affect

00:31:19.000 --> 00:31:19.320
people.

00:31:19.320 --> 00:31:19.560
Right.

00:31:19.560 --> 00:31:23.100
If you don't specify a column or a field of that type, it doesn't matter.

00:31:23.100 --> 00:31:24.440
You'll never know it or care.

00:31:24.920 --> 00:31:25.000
Yeah.

00:31:25.000 --> 00:31:25.340
Yeah.

00:31:25.340 --> 00:31:28.960
So there's a couple of comments in the live stream.

00:31:28.960 --> 00:31:33.340
And I think maybe we can go ahead and touch on them about sort of future plans.

00:31:33.340 --> 00:31:37.440
So you did mention that there's going to be kind of a major upgrade of V2.

00:31:37.440 --> 00:31:43.260
And Carlos out there asks, is there any plans for Pydantic to give support for PySpark data

00:31:43.260 --> 00:31:44.760
frame scheme validation?

00:31:44.760 --> 00:31:50.100
Or, you know, let me ask more broadly, like any of the data science world integration with

00:31:50.100 --> 00:31:53.580
like Pandas or other, you know, NumPy or other things like that?

00:31:53.580 --> 00:32:00.140
There's been a lot of issues on NumPy arrays and like validating them using list types without

00:32:00.140 --> 00:32:05.800
going all the way to a like Python list because that can have performance problems.

00:32:05.800 --> 00:32:10.300
I can't remember because it was a long time ago, but people, whoever it was, found a solution.

00:32:10.300 --> 00:32:13.600
And like Pydantic is used a lot now in data science.

00:32:13.700 --> 00:32:18.000
If you look at the projects it's used in by Uber and by Facebook, they're like big machine

00:32:18.000 --> 00:32:19.000
learning projects.

00:32:19.000 --> 00:32:23.040
Fast Facebook's fast MRI library uses it.

00:32:23.040 --> 00:32:27.360
Like it's used a reasonable amount in like big data validation pipelines.

00:32:27.360 --> 00:32:30.460
So I don't know about PySpark.

00:32:30.460 --> 00:32:33.400
So I'm not going to be able to give a definitive answer for that.

00:32:33.400 --> 00:32:38.040
If you create an issue, I'll endeavor to remember to look at it and have a look and give an answer.

00:32:38.040 --> 00:32:40.200
But you'll start your PySpark research project.

00:32:40.200 --> 00:32:40.580
Yeah.

00:32:41.520 --> 00:32:41.760
Nice.

00:32:41.760 --> 00:32:45.880
Also from Carlos really is like, what's the timeframe for V2?

00:32:45.880 --> 00:32:50.060
Someone joked to me the other day that the release date was originally put down as the end of March

00:32:50.060 --> 00:32:52.300
2020 and that didn't get reached.

00:32:52.300 --> 00:32:56.560
And it's still, the short answer is that like, I need to, there are two problems.

00:32:56.560 --> 00:33:01.020
One is I need to set some time aside to sit there and build quite a lot of code.

00:33:01.020 --> 00:33:04.060
Second problem is the number of open PRs and the number of issues.

00:33:04.060 --> 00:33:09.640
I find it hard sometimes to bring myself to go and work on Pydantic when I have time off,

00:33:09.640 --> 00:33:13.780
because a lot of it is like the trawl of going through issues and reviewing pull requests.

00:33:13.780 --> 00:33:20.400
And when I'm not doing my day job of writing code, I want to like write code on something fun and not have to review other people's code.

00:33:20.400 --> 00:33:22.540
Because I do that for a day job quite a lot.

00:33:22.680 --> 00:33:34.300
So I've had like a bit of trouble getting my like back ending gear to go and like work on Pydantic because I feel like there's 20 hours of reviewing other people's code before I can do anything fun.

00:33:34.300 --> 00:33:41.920
And I think one of the solutions to that is I'm just going to start building V2 and ignore some pull requests and might have to break some eggs to make an omelet.

00:33:42.080 --> 00:33:43.720
But I think that that's better.

00:33:43.720 --> 00:33:43.980
Okay.

00:33:43.980 --> 00:33:44.280
Yeah.

00:33:44.280 --> 00:33:49.460
Well, and also in your defense, a lot of things were planned for March 2020.

00:33:49.460 --> 00:33:49.920
Yeah.

00:33:49.920 --> 00:33:50.760
And got ready.

00:33:50.760 --> 00:33:51.300
That is true.

00:33:51.300 --> 00:33:51.860
That is true.

00:33:51.860 --> 00:33:55.920
I have sat at my desk in my office for a total of about eight hours since then.

00:33:55.920 --> 00:33:56.480
Yeah.

00:33:56.480 --> 00:33:58.620
So I haven't been back to the office in London at all.

00:33:58.620 --> 00:34:01.040
So, so yeah, I would hope this year.

00:34:01.140 --> 00:34:01.340
Yeah.

00:34:01.340 --> 00:34:01.660
Cool.

00:34:01.660 --> 00:34:06.720
Talk Python To Me is partially supported by our training courses.

00:34:06.720 --> 00:34:11.540
Do you want to learn Python, but you can't bear to subscribe to yet another service?

00:34:11.540 --> 00:34:14.780
At Talk Python Training, we hate subscriptions too.

00:34:14.780 --> 00:34:20.340
That's why our course bundle gives you full access to the entire library of courses for one fair price.

00:34:20.340 --> 00:34:21.460
That's right.

00:34:21.460 --> 00:34:27.400
With the course bundle, you save 70% off the full price of our courses and you own them all forever.

00:34:28.020 --> 00:34:33.520
That includes courses published at the time of the purchase, as well as courses released within about a year of the bundle.

00:34:33.520 --> 00:34:39.000
So stop subscribing and start learning at talkpython.fm/everything.

00:34:39.000 --> 00:34:47.380
And then related to that, a risky chance asks, where should people who want to contribute to PyDentix start?

00:34:47.380 --> 00:34:54.960
And I would help you kick off this conversation by just pointing out that you have tagged a bunch of issues as help wanted.

00:34:55.460 --> 00:34:58.780
And then also maybe reviewing PRs, but what else would you add to that?

00:34:58.780 --> 00:35:06.520
I think the first thing I would say is, and I know this isn't the most fun thing to do, but if people could help with reviewing discussions and issues.

00:35:06.520 --> 00:35:07.940
Like triage type stuff?

00:35:07.940 --> 00:35:14.620
Yeah, but if you go on to discussions, we use the GitHub discussions, which maybe people don't even see.

00:35:14.620 --> 00:35:18.340
But these are all questions you can go in and answer if someone has a problem.

00:35:18.340 --> 00:35:19.860
Lots of them aren't that complicated.

00:35:19.860 --> 00:35:24.220
I know that's perhaps not what risky chance meant in terms of writing code.

00:35:24.220 --> 00:35:26.260
And that's obviously, for some of us, where the fun lives.

00:35:26.260 --> 00:35:29.780
But these questions would be enormously helpful if people could help.

00:35:29.780 --> 00:35:31.800
You can see some of them are answered, and that's great.

00:35:31.800 --> 00:35:33.260
But there are others that aren't.

00:35:33.800 --> 00:35:38.480
And then, yeah, reviewing pull requests would be the second most useful thing that people could do.

00:35:38.480 --> 00:35:44.720
And then if there are help wanted issues, just checking that we're still on for it and it's the right time to do it.

00:35:44.720 --> 00:35:46.100
And then I do love submissions.

00:35:46.100 --> 00:35:50.480
I noticed today there were 200 and something people who've contributed to Pydantic.

00:35:50.480 --> 00:35:57.160
So I do do my best to support anyone who comes along, however inexperienced or experienced building features or fixing bugs.

00:35:57.320 --> 00:35:57.880
Yeah, fantastic.

00:35:57.880 --> 00:36:00.580
Another thing I want to talk to you about, is this the right one?

00:36:00.580 --> 00:36:01.520
I believe.

00:36:01.520 --> 00:36:03.400
No, the validating decorator.

00:36:03.400 --> 00:36:04.820
Well, let's talk about validators first.

00:36:04.820 --> 00:36:06.300
We touched on this a little bit.

00:36:06.300 --> 00:36:13.420
So one thing you can do is you can write functions that you decorate with this validator decorator.

00:36:13.420 --> 00:36:18.880
And it says, this is the function whose job is to do a deeper check on a field, right?

00:36:18.940 --> 00:36:24.040
So you can say this is a validator for name, this is a validator for username or validator for email or whatever.

00:36:24.040 --> 00:36:31.840
And those functions are ways in which you can take better control over what is a valid value and stuff like that, right?

00:36:31.840 --> 00:36:33.160
Yeah, but you can do more.

00:36:33.160 --> 00:36:37.060
You can't just be stricter, as in raising error, if it's not how you want it.

00:36:37.060 --> 00:36:42.320
You can also change the value that you're going to that's come in.

00:36:42.320 --> 00:36:51.040
So you can see in the first case of name contains a space, we check that the name doesn't contain a space as a dummy example, but we also return title.

00:36:51.040 --> 00:36:53.200
So capitalize the first letter.

00:36:53.320 --> 00:36:56.180
So you can also change the value you're going to put in.

00:36:56.180 --> 00:37:13.580
So coming back to the date case we were hearing about earlier, if you knew your users were going to use some very specific date format of day of the week as a string, followed by day of the month, followed by year in Roman numerals, you could spot that with a regex, have your own logic to do the validation.

00:37:13.580 --> 00:37:20.080
And then if it's any other date, pass it through to the normal pydantic logic, which will carry on and do its normal stuff on strings.

00:37:20.080 --> 00:37:34.920
Cool. Now this stuff is pretty advanced, but you can also do simple stuff like set an inner class, which is a config, and just set things like any string strip off the white space or lowercase all the strings or stuff like that, right?

00:37:34.920 --> 00:37:38.500
Yeah. And there's allow mutation, which you've got to there, which is super helpful.

00:37:38.500 --> 00:37:40.880
That's where we can stop fields from being modified.

00:37:40.880 --> 00:37:47.120
There's extra there, which is something people often want, which is what do we do with extra fields that we haven't defined on our model?

00:37:47.600 --> 00:37:55.400
Do we, is that an error? Do we just ignore them or do we allow them and just like bung them on the class and we won't have any type hints for them, but they are there if we want them.

00:37:55.400 --> 00:38:04.600
Yeah, very cool. Okay. So the other thing I wanted to ask you about is really interesting because part of what I think makes pydantic really interesting is its deep leveraging of type hints, right?

00:38:04.600 --> 00:38:18.240
And in Python, type hints are a suggestion. They're things that make our editors light up. They are things that if you really, really tried, I don't think most people do this, but you could run something like mypy against it and it would tell you if it's accurate or not.

00:38:18.300 --> 00:38:26.320
I think most people just put it as there's extra information, you know, maybe PyCharm or VS Code tells you you're doing it right or gives you better autocomplete.

00:38:26.460 --> 00:38:37.860
But under no circumstance or almost no circumstance does having a function called add that says x colon int comma y colon int only work if you pass integers, right?

00:38:37.860 --> 00:38:47.440
You could pass strings to it and probably get a concatenated string out of that Python function because there's no, it's not like C++ or something where it compiles down and checks the thing right.

00:38:47.440 --> 00:38:57.700
But you also have this validating decorator thing, which it seems to me like this will actually sort of add that runtime check for the types. Is that correct?

00:38:57.700 --> 00:39:09.840
That's exactly what it's what it's designed to do. It's always been a kind of like interest to me almost a kind of, yeah, just a kind of experiment to see whether this is possible, whether we could like have semi strictly typed logic in Python.

00:39:09.840 --> 00:39:26.300
I should say before we go any further, this isn't to be used on like every function. It's not like Rust where doing that validation actually makes it faster. This is going to make calling your function way, way slower because inside validate arguments, we're going to go off and do a whole bunch of logic to validate every field.

00:39:26.300 --> 00:39:36.880
But there are situations where it can be really useful and where creating that Pydantic model was a bit onerous, but where we can just bang on the decorator and get some validation kind of for free.

00:39:36.880 --> 00:39:44.740
Right. Because the decorator basically does the same thing. I mean, sorry, the classes do the same thing as this decorator might, but instead of having a class, you have arguments.

00:39:44.740 --> 00:40:00.320
And under the hood, what validate arguments is doing is it's expecting that function, taking out the arguments, building them into a Pydantic model and then running the input against that into that Pydantic model and then using the result to call on the, to call the function.

00:40:00.320 --> 00:40:05.160
Yeah. And that sounds like more work than just calling the function for sure. It depends on how much it does, right?

00:40:05.260 --> 00:40:09.980
Does it cache that kind of, does it like cache the class that it creates when it decorates a function?

00:40:09.980 --> 00:40:10.840
It caches the model.

00:40:10.840 --> 00:40:11.100
Yeah.

00:40:11.100 --> 00:40:19.660
Same as we do in other places, but yes, it's still a lot more like Pydantic fast for data validation, but it's data validation, not a compiler.

00:40:19.660 --> 00:40:28.520
Yeah. So maybe this would make sense if I'm writing a data science library and at the very outer shell, I pass in a whole bunch of data, then it goes off to all sorts of places.

00:40:28.780 --> 00:40:33.620
Maybe it might make sense to put this on the boundary entry point type of thing, but nowhere else.

00:40:33.620 --> 00:40:45.160
Yeah, exactly. Where someone's going to find it much easier to see a Pydantic error saying these fields were wrong rather than seeing some strange matrix that comes out the wrong shape because they passed in something as a, as a string, not an int.

00:40:45.160 --> 00:40:48.460
Or none type has no attribute such and such.

00:40:48.460 --> 00:40:49.000
Yeah.

00:40:49.000 --> 00:40:49.300
Whatever.

00:40:49.300 --> 00:40:50.020
Right.

00:40:50.060 --> 00:40:51.840
That standard error they always run into.

00:40:51.840 --> 00:40:52.560
Okay.

00:40:52.560 --> 00:40:53.800
That's pretty interesting.

00:40:53.800 --> 00:40:56.580
Let's talk a little bit about speed.

00:40:56.580 --> 00:41:03.400
You have talked about this a couple of times, but maybe it's just worth throwing up a simple example here to put them together.

00:41:03.400 --> 00:41:08.680
So we've got Pydantic, we've got Adders, we've got Valadeer, which I've never heard about, but very cool.

00:41:08.680 --> 00:41:13.180
Marshmallow and a couple of others like Django REST Framework and Cerebus.

00:41:13.820 --> 00:41:23.820
So it has the, all of these in relative time to some benchmark code that you have, but it basically gives it as a percentage or a factor of performance, right?

00:41:23.820 --> 00:41:24.200
Yeah.

00:41:24.200 --> 00:41:28.280
And the first thing I'll say is that there were lies, damn lies and benchmarks.

00:41:28.280 --> 00:41:37.780
Like you'll get, you might well get different results, but my impression from what I've seen is that Pydantic is as fast, if not faster than the other ways of doing it in Python.

00:41:38.120 --> 00:41:44.820
Short of writing your own custom code in each place to be like, yeah, to do manual validation, which is a massive pain.

00:41:44.820 --> 00:41:48.140
And if you're doing that, you probably want to go and write it in a proper compiled language anyway.

00:41:48.140 --> 00:41:48.480
Right.

00:41:48.480 --> 00:41:49.000
Right.

00:41:49.000 --> 00:41:53.740
Or maybe just use Cython on some little section, something like that, right?

00:41:53.740 --> 00:41:58.000
So all of Pydantic is compiled with Cython and is about twice as fast.

00:41:58.200 --> 00:42:02.720
If you install it with pip, you will get mostly the compiled version.

00:42:02.720 --> 00:42:12.460
There are binaries available for Windows, Mac and Linux, Windows 64-bit, not 32, and maybe some other extreme, and it will compile for other operating systems.

00:42:12.460 --> 00:42:16.360
So it's already faster than just calling Python.

00:42:16.360 --> 00:42:23.360
Well, I don't know about whether the validation with Pydantic that's compiled is faster than raw Python, but like it'll be of the same order of magnitude.

00:42:23.360 --> 00:42:23.700
Yeah.

00:42:23.700 --> 00:42:24.100
Yeah.

00:42:24.100 --> 00:42:24.520
Fantastic.

00:42:24.520 --> 00:42:24.920
Okay.

00:42:24.940 --> 00:42:26.820
I didn't realize it was compiled with Cython.

00:42:26.820 --> 00:42:27.280
That's great.

00:42:27.280 --> 00:42:27.620
Yeah.

00:42:27.620 --> 00:42:28.520
It's part of the magic, huh?

00:42:28.520 --> 00:42:29.140
Making it faster.

00:42:29.140 --> 00:42:29.520
Yeah.

00:42:29.520 --> 00:42:36.340
So that was David Montague a year and a half ago, put an enormous amount of effort into it and, yeah, about doubled the performance.

00:42:36.340 --> 00:42:40.440
It's Python compiled with Cython rather than real Cython code.

00:42:40.440 --> 00:42:44.500
So it's not as C speed, but it's faster than just calling Python.

00:42:44.500 --> 00:42:45.280
Yeah, absolutely.

00:42:45.280 --> 00:42:54.900
And Cython, taking the type hint information and working so well with it these days, it probably was easier than it used to be or didn't require as many changes as it might.

00:42:54.900 --> 00:42:56.400
Otherwise, I think it's an open question.

00:42:56.400 --> 00:42:58.200
I think it's an open question whether Cython is faster with type hints.

00:42:58.200 --> 00:42:58.720
It does.

00:42:58.720 --> 00:43:05.120
It's in places actually adding type hints makes it slower because it does its own checks that I think is a string when you've said it's a string.

00:43:05.120 --> 00:43:07.240
But yeah, I think it does use it in places.

00:43:07.240 --> 00:43:07.540
Yeah.

00:43:07.540 --> 00:43:10.100
I was thinking more like you don't have to rewrite it in Cython.

00:43:10.100 --> 00:43:15.460
I don't have to convert Python code to Cython code where it has its own sort of descriptor language.

00:43:15.840 --> 00:43:21.560
But like if you have Python code that's type annotated, it'll take that and run with it these days.

00:43:21.560 --> 00:43:25.180
I think it isn't any faster or any better because of the type hints much.

00:43:25.180 --> 00:43:27.720
Although someone out there is an expert and I don't want to say that.

00:43:27.720 --> 00:43:28.400
So I'm not sure.

00:43:28.400 --> 00:43:29.360
Yeah, I hear you.

00:43:29.360 --> 00:43:30.260
All right.

00:43:30.260 --> 00:43:33.220
Another thing I want to touch on is the data model code generator.

00:43:33.220 --> 00:43:34.160
You want to tell us about this thing?

00:43:34.160 --> 00:43:34.680
What is this?

00:43:34.680 --> 00:43:36.340
I haven't used it much.

00:43:36.340 --> 00:43:37.880
But yes, what we haven't talked about here.

00:43:37.880 --> 00:43:38.700
It's just what it is.

00:43:38.800 --> 00:43:45.340
Is JSON Schema, which is what Sebastian Ramirez implemented a couple of years ago when he was first starting out on FastAPI.

00:43:45.340 --> 00:43:47.860
And it's one of the coolest features of FastAPI.

00:43:47.860 --> 00:43:54.120
And Pydantic is that once you've created your model, you don't just get model and model validation.

00:43:54.120 --> 00:43:56.640
You also get a schema generated for your model.

00:43:56.640 --> 00:44:01.140
And in FastAPI, that's automatically created with Redock into really smart documentation.

00:44:01.140 --> 00:44:08.200
So you don't even have to think about documentation most of the time if it's internal or it's not widely used API.

00:44:08.200 --> 00:44:16.020
And even if it's widely used, add some doc strings and you've got yourself like amazing API documentation, just straight from your model.

00:44:16.020 --> 00:44:23.320
And data model code generation, as I understand it, is generating those JSON schema schemas for models.

00:44:23.320 --> 00:44:24.000
Is that right?

00:44:24.000 --> 00:44:24.840
Yeah, I think so.

00:44:24.840 --> 00:44:29.600
It feels to me like it's the reverse of what you described from what Sebastian has created, right?

00:44:29.600 --> 00:44:35.800
Like given one of these open API definitions, it will generate the Pydantic model for you.

00:44:36.340 --> 00:44:42.040
So if I was going to consume an API and I'm like, well, I got to write some Pydantic models to match it.

00:44:42.040 --> 00:44:47.680
Like you could run this thing to say, well, give me a good shot at getting pretty close to what it's going to be.

00:44:47.680 --> 00:44:47.920
Yeah.

00:44:47.920 --> 00:44:48.280
Yeah.

00:44:48.280 --> 00:44:48.680
Yeah.

00:44:48.680 --> 00:44:49.680
I had it around the wrong way.

00:44:49.680 --> 00:44:54.960
But yeah, my instinct is I haven't used it, but that it gets you, it does 90% of the work for you.

00:44:55.000 --> 00:44:59.860
And then there's a bit of like manual tinkering around the edge to change some of the types I suspect.

00:44:59.860 --> 00:45:01.640
But like, yeah, really useful.

00:45:01.640 --> 00:45:01.920
Yeah.

00:45:01.920 --> 00:45:03.680
And it supports different frameworks and stuff.

00:45:03.680 --> 00:45:11.500
And I haven't used it either, but it just seemed like it was a cool thing related to sort of quickly get people started if they've got something complex to do with Pydantic.

00:45:11.700 --> 00:45:19.520
So for example, I built this weather, real time, weather, live weather data service for one of my classes over at weather.talkpython.fm.

00:45:19.520 --> 00:45:23.900
I built that in FastAPI and it exchanges Pydantic models.

00:45:23.900 --> 00:45:27.940
And all you got to do in order to see the documentation, just go to slash docs.

00:45:28.120 --> 00:45:31.120
And then it gives you the JSON schema.

00:45:31.120 --> 00:45:36.140
So presumably I could point that thing at this and then it would generate.

00:45:36.140 --> 00:45:37.880
And go back to the model.

00:45:37.880 --> 00:45:38.280
Exactly.

00:45:38.280 --> 00:45:42.960
And get a fairly complicated Pydantic model prebuilt for me, which I think is pretty excellent.

00:45:42.960 --> 00:45:43.280
Yeah.

00:45:43.280 --> 00:45:51.300
It's worth saying maybe you disagree, but I think the redock version of the documentation or autodox is even smarter than that one.

00:45:51.300 --> 00:45:52.040
I don't know if you've got it.

00:45:52.040 --> 00:45:53.240
Yeah, that one I think is even smarter.

00:45:53.240 --> 00:45:54.880
Oh yeah, this is a really nice one.

00:45:54.880 --> 00:45:55.700
I like this one a lot.

00:45:55.800 --> 00:45:57.700
Yeah, it even gives you the responses there.

00:45:57.700 --> 00:46:02.620
It could be 200 or 422, which I did build that into there, but I didn't expect it to actually know.

00:46:02.620 --> 00:46:03.300
That's pretty interesting.

00:46:03.300 --> 00:46:03.980
Yeah, it's cool.

00:46:03.980 --> 00:46:04.700
Yeah, it's very cool.

00:46:04.700 --> 00:46:08.540
So they're both there, either slash docs or slash redock.

00:46:08.540 --> 00:46:10.040
FastAPI will pull them.

00:46:10.040 --> 00:46:12.740
You can switch one off or change the endpoints, but yeah.

00:46:12.740 --> 00:46:13.000
Yeah.

00:46:13.000 --> 00:46:18.040
And by the way, if you're putting out a FastAPI and you don't want public documentation,

00:46:18.040 --> 00:46:22.560
make sure that you set docs, the docs URL and the redox URL to none.

00:46:22.560 --> 00:46:25.860
And when you're creating your app or your API instance.

00:46:25.860 --> 00:46:28.400
So yeah, that's always on unless you take action.

00:46:28.400 --> 00:46:29.960
So you better be sure you want to.

00:46:29.960 --> 00:46:36.000
Or you can do what I've done, which is protect it with authentication so the front end developers can use it, but it's not publicly available.

00:46:36.000 --> 00:46:43.220
So if you're building like a React app, it's really useful to have your front end engineers be able to go and see that stuff and understand what the fields are.

00:46:43.460 --> 00:46:47.620
But it's a bit of a weird thing to make public, even if there's nothing particularly sensitive.

00:46:47.620 --> 00:46:49.400
So yeah, you can put it behind authentication.

00:46:49.400 --> 00:46:49.880
Yeah.

00:46:49.880 --> 00:46:50.740
Yeah, very good.

00:46:50.740 --> 00:46:51.280
All right.

00:46:51.280 --> 00:46:57.480
You already talked about the PyCharm plugin, but maybe give us a sense for why do we need a PyCharm plugin?

00:46:57.480 --> 00:46:58.740
I have PyCharm.

00:46:59.260 --> 00:47:04.320
And if it has the type information, a lot of times it seems like it's already good to go.

00:47:04.320 --> 00:47:06.040
So what do I get from this PyCharm plugin?

00:47:06.040 --> 00:47:07.360
Like, why should I go put this in?

00:47:07.440 --> 00:47:18.340
So once you've created your model, if we think about the example on the index page again, we would, once we've created our model, accessing .friends or .id or .name will work.

00:47:18.340 --> 00:47:28.000
And PyCharm will correctly give us information about, we'll say, okay, first name exists, like foobar name doesn't exist.

00:47:28.000 --> 00:47:30.900
It's a string, so it makes sense to add it to another string.

00:47:31.400 --> 00:47:40.940
But when we initialize a model, it doesn't know how, like, the init function of Pydantic just looks like, take all of the things and pass them to some magic function that will then do the valid thing.

00:47:40.940 --> 00:47:41.340
I see.

00:47:41.340 --> 00:47:45.200
It looks like star, star, KWR, good luck, go read the docs.

00:47:45.200 --> 00:47:46.360
Yeah, exactly that.

00:47:46.360 --> 00:47:51.840
But this is where the PyCharm plugin comes in because it gives you documentation on the arguments.

00:47:51.840 --> 00:48:00.180
Okay, so it looks at the fields and their types and says, well, these are actually keyword arguments to the constructor, the initializer.

00:48:00.180 --> 00:48:00.480
Yeah.

00:48:00.580 --> 00:48:00.840
Okay.

00:48:00.840 --> 00:48:01.920
Yeah, got it.

00:48:01.920 --> 00:48:02.580
That's very cool.

00:48:02.580 --> 00:48:06.200
And it will also, I don't even know what it does.

00:48:06.200 --> 00:48:07.640
I just use it the whole time and it works.

00:48:07.640 --> 00:48:09.520
You know those things, but you don't even think about them.

00:48:09.520 --> 00:48:10.220
Yeah, cool.

00:48:10.220 --> 00:48:14.840
So it gives you auto-completion and type checking, which is cool for the initializer, right?

00:48:14.840 --> 00:48:17.540
So if you were to try to pass in something wrong, it lets you know.

00:48:17.540 --> 00:48:19.680
Also, it says it supports refactoring.

00:48:19.680 --> 00:48:21.980
If you refactor the keyword.

00:48:21.980 --> 00:48:28.920
One of the really useful things it does is when we talked about validators, which are done by a decorator, they are class methods.

00:48:28.920 --> 00:48:33.760
It's very specifically because you might think that they're instance methods and you have access to self.

00:48:33.760 --> 00:48:37.160
You don't because they're called before the model itself is initialized.

00:48:37.160 --> 00:48:40.800
So the first argument to them should be class CLS.

00:48:40.920 --> 00:48:55.560
It will automatically give you an error if you put self, which is really helpful when you're creating those validators because otherwise without it, HighCharm assumes it's an instance method, gives you self, and then you get yourself into hot water when you access self.userid and it breaks.

00:48:55.560 --> 00:48:56.220
Oh, interesting.

00:48:56.220 --> 00:48:56.560
Okay.

00:48:56.560 --> 00:48:57.740
Yeah, that makes a lot of sense.

00:48:57.740 --> 00:49:03.960
Because it's converting and checking all the values and then it creates the object and assigns the fields, right?

00:49:04.100 --> 00:49:13.840
Yeah, so we can access other values during validation from the values keyword argument to the validator, but not via like self.userid or whatever.

00:49:13.840 --> 00:49:14.620
Yeah, cool.

00:49:14.620 --> 00:49:18.680
And Risky Chance loves that it works with aliases too, which is pretty cool.

00:49:18.680 --> 00:49:19.160
Oh, yeah.

00:49:19.160 --> 00:49:19.800
It does.

00:49:19.800 --> 00:49:21.300
It does lots of cool things.

00:49:21.300 --> 00:49:22.500
I'm really impressed by it.

00:49:22.500 --> 00:49:24.880
It's one of the coolest things that come out of Pydantic.

00:49:24.880 --> 00:49:25.260
Awesome.

00:49:25.260 --> 00:49:32.820
Yeah, I've installed it and I'm like, I'm sure my Pydantic experience is better, but I just don't know what is built in and what is coming from this thing.

00:49:32.820 --> 00:49:33.740
So, yeah, that's...

00:49:33.740 --> 00:49:38.240
We're also used to PyCharm just working on so many things that you don't even notice.

00:49:38.240 --> 00:49:40.760
Like, yeah, you only notice when it doesn't work, so...

00:49:40.760 --> 00:49:41.460
Yeah, absolutely.

00:49:41.460 --> 00:49:53.400
So, we're getting a little short on time, but I did want to ask you about Python DevTools because you talked about having Pydantic work well with DevTools as well.

00:49:53.400 --> 00:49:53.780
Yeah.

00:49:53.780 --> 00:49:54.640
What are these?

00:49:54.640 --> 00:49:56.900
You are also the author of Python DevTools, yeah?

00:49:56.900 --> 00:49:57.680
Yeah, what is this?

00:49:57.680 --> 00:50:03.280
For me, it's just a debug print command that puts stuff pretty and gives it color and tells me what line it was printed on.

00:50:03.280 --> 00:50:05.800
And I use it the whole time in development instead of print.

00:50:05.800 --> 00:50:11.340
And obviously, I wanted it to show me my Pydantic models in a pretty way, so it has integration.

00:50:11.340 --> 00:50:15.740
There are some hooks in DevTools that allow it to customize the way stuff's printed.

00:50:16.400 --> 00:50:23.360
And I actually know that the author of Rich, he slightly frustratingly has used a different system all over again, but he's also supported Pydantic.

00:50:23.360 --> 00:50:27.520
So, Pydantic will also print pretty with Rich as well as with DevTools.

00:50:27.520 --> 00:50:28.160
Yeah, cool.

00:50:28.160 --> 00:50:29.200
Okay, really nice.

00:50:29.200 --> 00:50:34.260
Yeah, Rich is a great TUI, Terminal User Interface Library for Python.

00:50:34.260 --> 00:50:35.020
Yeah, it's cool.

00:50:35.020 --> 00:50:36.080
It's different from DevTools.

00:50:36.080 --> 00:50:37.100
I wouldn't say they compete.

00:50:37.100 --> 00:50:38.600
DevTools is, for me, it's just...

00:50:38.600 --> 00:50:45.320
It does have some other things, some timing tools and some formatting, but for me, it's just the debug print command that Python never had.

00:50:45.320 --> 00:50:45.580
Nice.

00:50:45.580 --> 00:50:50.120
So, what's the Pydantic plugin here, or connection, rather, here?

00:50:50.120 --> 00:50:55.800
So, if I debug out of DevTools a model, I get just a really nice representation?

00:50:55.800 --> 00:50:56.940
Yeah, exactly that.

00:50:56.940 --> 00:50:58.640
It's not showing it...

00:50:58.640 --> 00:50:59.940
It's because you're in the DevTools docs.

00:50:59.940 --> 00:51:02.380
There's some other docs in Pydantic to give you an example.

00:51:02.380 --> 00:51:07.280
But it'll give you a nice example if it expanded out rather than squashed into one line.

00:51:07.520 --> 00:51:09.300
So, usage with DevTools is the last...

00:51:09.300 --> 00:51:09.340
There you go.

00:51:09.340 --> 00:51:09.860
Got it.

00:51:09.860 --> 00:51:11.300
Yeah, so you see that.

00:51:11.300 --> 00:51:15.240
The user picked out nicely instead of, yeah, done like that.

00:51:15.240 --> 00:51:16.740
I suppose that's kind of...

00:51:16.740 --> 00:51:18.780
That demonstrates its usage for me.

00:51:18.780 --> 00:51:19.400
Yeah, perfect.

00:51:19.400 --> 00:51:20.260
That looks really good.

00:51:20.260 --> 00:51:25.540
It's nice to be able to just print out these sorts of things and see them really quickly.

00:51:25.540 --> 00:51:29.380
What's the just basic string representation of a Pydantic model?

00:51:29.380 --> 00:51:37.380
For example, if I'm in PyCharm and I hit a breakpoint, or I'm just curious what something is and I just print it, PyCharm will put a little grayed out...

00:51:37.380 --> 00:51:39.740
String, str representation.

00:51:39.740 --> 00:51:40.680
It's right there.

00:51:40.680 --> 00:51:42.980
I think that's the string representation you're looking at right there.

00:51:42.980 --> 00:51:43.500
Yeah, perfect.

00:51:43.500 --> 00:51:48.100
So, you get a really rich sort of view of it embedded in the editor or if you print it...

00:51:48.100 --> 00:51:51.600
And if you use Repra, then you get basically wrapped in user.

00:51:51.600 --> 00:51:51.940
All right.

00:51:51.940 --> 00:51:52.160
Okay.

00:51:52.160 --> 00:51:53.040
So, it gives you...

00:51:53.040 --> 00:51:53.840
As if it were...

00:51:53.840 --> 00:51:54.040
Yeah.

00:51:54.160 --> 00:51:56.160
You're trying to construct it out of that data.

00:51:56.160 --> 00:51:56.860
Yeah.

00:51:56.860 --> 00:51:57.160
Okay.

00:51:57.160 --> 00:51:57.920
Fantastic.

00:51:57.920 --> 00:52:02.200
Well, you know, we've covered a bunch of things and I know there's a lot more.

00:52:02.200 --> 00:52:11.140
I don't recall whether we talked about this while we were recording or whether we talked about it before and we were just setting up what we wanted to talk about.

00:52:11.220 --> 00:52:16.400
But it's worth emphasizing that this is not just a FastAPI validation data exchange thing.

00:52:16.400 --> 00:52:17.220
It works really great.

00:52:17.220 --> 00:52:18.580
A lot of the stuff happens there.

00:52:18.580 --> 00:52:22.320
But if you're using Flask, if you're using Pyramid, if you're using...

00:52:22.320 --> 00:52:25.780
I don't know about Django so much because of the models and stuff there, but...

00:52:25.780 --> 00:52:26.240
There is.

00:52:26.240 --> 00:52:28.180
Someone's built Django admin.

00:52:28.180 --> 00:52:33.400
So, one of the things we haven't talked about as well is settings management, which Pydantic has some pretty powerful features for.

00:52:33.480 --> 00:52:40.640
And actually, one of the things was added in 1.7 or 1.8 was basically a system for plugins to do even crazier stuff with settings.

00:52:40.640 --> 00:52:47.020
So, not just loading them from environment variables and from .m files, but also from Docker secrets.

00:52:47.020 --> 00:52:49.900
Now we have an interface to load them from kind of anywhere.

00:52:49.900 --> 00:52:53.540
So, you can build your own interface for loading settings from places.

00:52:53.540 --> 00:53:00.600
But someone's built a Django settings tool with Pydantic to kind of validate your Django settings using Pydantic.

00:53:00.760 --> 00:53:07.000
But yeah, I think it's what's cool about Pydantic is it's not part of a kind of walled garden of tools that all fit together.

00:53:07.000 --> 00:53:08.900
Well, that have to be used with each other.

00:53:08.900 --> 00:53:12.340
It fits with Pydantic, but it's used in lots of other big projects.

00:53:12.340 --> 00:53:16.140
Or you can just use it in Flask or in Django or wherever you like.

00:53:16.140 --> 00:53:16.320
Right.

00:53:16.320 --> 00:53:20.860
If you're reading JSON files off a disk, it could totally make sense to use it.

00:53:20.860 --> 00:53:23.820
Or you're doing screen scraping, potentially it makes sense.

00:53:23.820 --> 00:53:26.700
Or just calling API, but you're the client of that API.

00:53:26.700 --> 00:53:28.160
It could totally make sense to do that.

00:53:28.160 --> 00:53:28.400
Yeah.

00:53:28.400 --> 00:53:28.700
Yeah.

00:53:28.700 --> 00:53:34.900
I just want to point out, like it's super broadly applicable, not just where people see it being really used.

00:53:34.900 --> 00:53:38.080
And Nick H out there is definitely going to try this with Django.

00:53:38.080 --> 00:53:38.840
So, awesome.

00:53:38.840 --> 00:53:39.380
That's cool.

00:53:39.380 --> 00:53:39.860
Yeah.

00:53:39.860 --> 00:53:45.720
So, let's wrap this up with just, I know we spoke a little bit about V2 and the timing.

00:53:45.720 --> 00:53:51.900
Like, what are the major features that you think, like what are the highlights that people should look forward to or be excited about?

00:53:51.900 --> 00:53:56.840
There's a bigger problem at hand, which is the Python 3.10 at the moment in PEP.

00:53:56.840 --> 00:53:59.740
I'm going to try and remind myself of the exact number.

00:53:59.740 --> 00:54:08.120
But like in PEP 6.9, no, in 5.6.3, basically all type hints become strings instead of Python objects.

00:54:08.120 --> 00:54:10.900
And so, and like that's been available in future.

00:54:10.900 --> 00:54:11.380
Right.

00:54:11.440 --> 00:54:15.200
Is that the lazy evaluation of the annotation, something like that?

00:54:15.200 --> 00:54:17.140
But it's not even a lazy evaluation.

00:54:17.140 --> 00:54:18.100
It's a non-evaluation.

00:54:18.100 --> 00:54:30.180
And it seems like unless Python themselves, the core team, are prepared to move on this and like be practical about things, it might be that Pydantic becomes either like hard to use or even not useful in 3.10.

00:54:30.340 --> 00:54:39.860
It sounds like I'm talking at the Python Summit in PyCon US in May in the like language in the bit where people discuss it.

00:54:39.860 --> 00:54:41.080
And I'm going to try and put this forward.

00:54:41.080 --> 00:54:48.460
But like I had a conversation today just before I came online now with someone who's created a PEP that should fix this.

00:54:48.460 --> 00:54:51.860
But the current response from the core developers is to refuse it.

00:54:51.860 --> 00:54:55.220
So I'm like really worried and frustrated that might happen.

00:54:55.220 --> 00:55:05.240
And lots of tools, FastAPI, Pydantic, Typer, and others, again, it get broken for the sake of principle effectively that type hints should only be used for static type analysis.

00:55:05.240 --> 00:55:06.160
So we'll see what happens.

00:55:06.160 --> 00:55:08.580
And normally with open source, people find a way around.

00:55:08.580 --> 00:55:10.160
But like I think that's really worrying.

00:55:10.160 --> 00:55:12.880
And I'll create an issue on Pydantic to track this properly.

00:55:12.880 --> 00:55:14.580
But it is something to be aware of.

00:55:14.580 --> 00:55:22.200
And it's something that like I think those of us who use these libraries need to like it's very easy to wait until after something's released and then be frustrated.

00:55:22.200 --> 00:55:25.940
It's important sometimes to notice before they're released and make a point.

00:55:25.940 --> 00:55:26.200
Wow.

00:55:26.200 --> 00:55:27.520
Well, I'm really glad you pointed that out.

00:55:27.520 --> 00:55:28.280
I had no idea.

00:55:28.280 --> 00:55:33.960
I mean, I knew there were minor behind the scenes changes from a consumer perspective of type annotations.

00:55:33.960 --> 00:55:38.200
But that sounds like there's more going on for libraries like this.

00:55:38.200 --> 00:55:45.440
There's a PEP that will fix this, which is PEP 649, which I have not yet read because I only got the email about it two hours ago.

00:55:45.440 --> 00:55:50.380
But if anyone's looking into it, I will create an issue on Pydantic to talk about this.

00:55:50.380 --> 00:55:52.560
But something like this needs to happen.

00:55:52.560 --> 00:55:55.980
Also, Larry emailed me an hour or two hours ago to talk about this.

00:55:55.980 --> 00:56:02.960
But this is a really big problem that we need to like prevent like breaking lots of cool stuff that's happening in Python.

00:56:02.960 --> 00:56:03.500
All right.

00:56:03.500 --> 00:56:04.640
Well, I agree.

00:56:05.500 --> 00:56:14.380
First impressions is I absolutely agree because I do think what you guys are doing, what you're doing with Pydantic, what is happening with FastAPI and these types of systems.

00:56:14.380 --> 00:56:21.180
It's a really fantastic direction and really building on top of the type annotation world.

00:56:21.180 --> 00:56:23.540
And I would hate to see that get squashed.

00:56:23.540 --> 00:56:28.200
What's incredible about it just briefly is that it's used by Microsoft in core bits of Office.

00:56:28.200 --> 00:56:32.160
It's used by Uber, by the NSA, by like banks.

00:56:32.160 --> 00:56:33.860
It's used by JP Morgan.

00:56:34.500 --> 00:56:37.240
But it's also really easy to get started with at the very beginning.

00:56:37.240 --> 00:56:45.800
And it's wonderful for me that we can build open source code that can be useful to like the biggest organizations in the world and to someone when they're first getting started.

00:56:45.800 --> 00:56:52.320
Not this idea that it has to be like dense and mainframe and impossible or like Mickey Mouse and not worth using.

00:56:52.320 --> 00:56:52.560
Right.

00:56:52.560 --> 00:56:55.540
Like FastAPI and Pydantic seem to be managing to be both.

00:56:55.540 --> 00:56:55.960
I agree.

00:56:55.960 --> 00:56:57.180
I think they are absolutely.

00:56:57.440 --> 00:57:00.520
So, well, congratulations on building something amazing.

00:57:00.520 --> 00:57:02.240
Thank you very much, Michael.

00:57:02.240 --> 00:57:06.180
And hopefully, Pep 649 keeps things rolling smooth.

00:57:06.180 --> 00:57:08.840
Hopefully that gets ironed out.

00:57:08.840 --> 00:57:09.440
All right.

00:57:09.440 --> 00:57:10.860
Now, we're pretty much out of time.

00:57:10.860 --> 00:57:13.180
But before I let you out of here, let me ask the final two questions.

00:57:13.180 --> 00:57:17.860
So if you're going to write some code, if you're going to work on Pydantic, what editor do you use?

00:57:17.860 --> 00:57:18.900
I use PyCharm.

00:57:18.900 --> 00:57:19.360
Right on.

00:57:19.760 --> 00:57:21.680
And the Pydantic plugin, I'm guessing.

00:57:21.680 --> 00:57:22.960
And the Pydantic plugin, yeah.

00:57:22.960 --> 00:57:24.380
Right on.

00:57:24.380 --> 00:57:32.980
And then if you've got a package out on PyPI that you think is interesting, maybe not the most popular, but you're like, oh, I ran across this thing that's amazing.

00:57:32.980 --> 00:57:34.380
You should know about it.

00:57:34.380 --> 00:57:36.600
I should probably not break the rule and talk about my own.

00:57:36.600 --> 00:57:39.760
But like DevTools, which I talked about, is incredibly useful to me.

00:57:39.760 --> 00:57:42.120
And so I would spread the word a bit on that.

00:57:42.120 --> 00:57:47.180
Other than that, I just do a shout out to all of those packages that people don't see that are the bedrock of everything.

00:57:47.340 --> 00:57:51.860
So from Coverage to Starlit, which is the other library that's the basis of FastAPI.

00:57:51.860 --> 00:57:52.780
Sebastian's great.

00:57:52.780 --> 00:57:57.640
And I mean, no offense to him, but he stands on the shoulders of people who've done lots of other things.

00:57:57.640 --> 00:57:58.940
And they're really, really powerful.

00:57:58.940 --> 00:58:01.060
So I would spare a bit of time for them.

00:58:01.060 --> 00:58:16.180
If you're thinking of sponsoring someone, think about sponsoring Ned who does Coverage or any of those other bits of pytest, all the workhorses that aren't particularly headlined, but are really, really valuable to all of our daily life writing code.

00:58:16.180 --> 00:58:18.860
Yeah. And I'm going to go with Vlad out there.

00:58:18.860 --> 00:58:22.380
He says, you need us know about PyTonic because of FastAPI.

00:58:22.380 --> 00:58:22.900
I agree.

00:58:22.900 --> 00:58:33.040
But FastAPI, as you pointed out, absolutely stands on top of Starlit, which there's just this whole chain of things that each one adds their own special sauce.

00:58:33.040 --> 00:58:34.700
But they're there because of...

00:58:34.700 --> 00:58:36.680
But I should say again, FastAPI is awesome.

00:58:36.680 --> 00:58:37.800
I didn't use it initially.

00:58:37.800 --> 00:58:41.920
I'm a contributor to aiohttp, which is also really cool.

00:58:41.920 --> 00:58:45.680
But I've, over the last year, become a complete convert to FastAPI.

00:58:45.680 --> 00:58:46.360
I use it.

00:58:46.360 --> 00:58:47.700
It's my go-to tool now.

00:58:47.700 --> 00:58:48.280
So it's awesome.

00:58:48.280 --> 00:58:48.520
Yeah.

00:58:48.520 --> 00:58:49.060
Fantastic.

00:58:49.580 --> 00:58:49.880
All right.

00:58:49.880 --> 00:58:51.640
Final call to action.

00:58:51.640 --> 00:58:53.680
People want to check out PyTonic.

00:58:53.680 --> 00:58:55.420
Maybe they want to contribute to PyTonic.

00:58:55.420 --> 00:58:56.020
What do you tell them?

00:58:56.020 --> 00:58:57.220
Go and have a read through the docs.

00:58:57.220 --> 00:58:59.520
And yeah, go from there.

00:58:59.520 --> 00:59:05.280
If you can make a tweet to the docs to make it easier to read, you can answer someone's question or even create a feature.

00:59:05.280 --> 00:59:05.960
That would be...

00:59:05.960 --> 00:59:06.340
That's awesome.

00:59:06.340 --> 00:59:06.580
Cool.

00:59:06.580 --> 00:59:07.020
All right.

00:59:07.100 --> 00:59:10.340
And if I'm not there immediately and I don't reply for weeks, I'm sorry.

00:59:10.340 --> 00:59:11.920
And I promise to as soon as I can.

00:59:11.920 --> 00:59:13.300
Fantastic.

00:59:13.300 --> 00:59:13.760
All right.

00:59:13.760 --> 00:59:14.800
Samuel, thanks for being on the show.

00:59:14.800 --> 00:59:19.560
It's been great to learn more deep information about PyTonic because it's so simple to use it.

00:59:19.560 --> 00:59:21.080
It's easy to just skim the surface.

00:59:21.080 --> 00:59:21.840
Awesome, Michael.

00:59:21.840 --> 00:59:22.500
Thank you very much.

00:59:22.500 --> 00:59:23.100
Yeah, you bet.

00:59:23.100 --> 00:59:23.560
Bye-bye.

00:59:23.560 --> 00:59:24.080
Yes, bye.

00:59:24.080 --> 00:59:27.620
This has been another episode of Talk Python To Me.

00:59:27.620 --> 00:59:29.900
Our guest in this episode was Samuel Colvin.

00:59:29.900 --> 00:59:34.300
And it's been brought to you by 45 Drives and us over at Talk Python Training.

00:59:35.340 --> 00:59:38.520
Solve your storage challenges with hardware powered by open source.

00:59:38.520 --> 00:59:46.840
Check out 45 Drives storage servers at talkpython.fm/45 Drives and skip the vendor lock-in and software licensing fees.

00:59:46.840 --> 00:59:48.700
Want to level up your Python?

00:59:48.700 --> 00:59:52.760
We have one of the largest catalogs of Python video courses over at Talk Python.

00:59:52.760 --> 00:59:57.940
Our content ranges from true beginners to deeply advanced topics like memory and async.

00:59:57.940 --> 01:00:00.600
And best of all, there's not a subscription in sight.

01:00:00.600 --> 01:00:03.500
Check it out for yourself at training.talkpython.fm.

01:00:03.580 --> 01:00:05.400
Be sure to subscribe to the show.

01:00:05.400 --> 01:00:08.180
Open your favorite podcast app and search for Python.

01:00:08.180 --> 01:00:09.480
We should be right at the top.

01:00:09.480 --> 01:00:18.860
You can also find the iTunes feed at /itunes, the Google Play feed at /play, and the direct RSS feed at /rss on talkpython.fm.

01:00:18.860 --> 01:00:22.280
We're live streaming most of our recordings these days.

01:00:22.280 --> 01:00:30.060
If you want to be part of the show and have your comments featured on the air, be sure to subscribe to our YouTube channel at talkpython.fm/youtube.

01:00:30.560 --> 01:00:31.980
This is your host, Michael Kennedy.

01:00:31.980 --> 01:00:33.260
Thanks so much for listening.

01:00:33.260 --> 01:00:34.420
I really appreciate it.

01:00:34.420 --> 01:00:36.340
Now get out there and write some Python code.

01:00:36.340 --> 01:00:36.400
Thank you.

01:00:36.400 --> 01:00:37.400
Bye.

01:00:37.400 --> 01:00:38.400
Bye.

01:00:38.400 --> 01:00:39.400
Bye.

01:00:39.400 --> 01:00:40.400
Bye.

01:00:40.400 --> 01:00:40.400
Bye.

01:00:40.400 --> 01:00:40.400
Bye.

01:00:40.400 --> 01:00:40.400
Bye.

01:00:40.400 --> 01:00:40.400
Bye.

01:00:40.400 --> 01:00:41.400
Bye.

01:00:41.400 --> 01:00:42.400
Bye.

01:00:42.400 --> 01:00:42.400
Bye.

01:00:42.400 --> 01:00:42.400
Bye.

01:00:42.400 --> 01:00:42.400
Bye.

01:00:42.400 --> 01:00:43.400
Bye.

01:00:43.400 --> 01:00:43.400
Bye.

01:00:43.400 --> 01:00:44.400
Bye.

01:00:44.400 --> 01:00:44.400
Bye.

01:00:44.400 --> 01:00:44.400
Bye.

01:00:44.400 --> 01:00:45.400
Bye.

01:00:45.400 --> 01:00:46.400
Bye.

01:00:46.400 --> 01:00:46.400
Bye.

01:00:46.400 --> 01:00:46.400
Bye.

01:00:46.400 --> 01:00:47.400
Bye.

01:00:47.400 --> 01:00:48.400
Bye.

01:00:48.400 --> 01:00:48.400
Bye.

01:00:48.400 --> 01:00:49.400
Bye.

01:00:49.400 --> 01:00:50.400
Bye.

01:00:50.400 --> 01:00:50.400
Bye.

01:00:50.400 --> 01:00:50.400
Bye.

01:00:50.400 --> 01:00:51.400
Bye.

01:00:51.400 --> 01:00:52.400
Bye.

01:00:52.400 --> 01:00:52.400
Bye.

01:00:52.400 --> 01:00:53.400
Bye.

01:00:53.400 --> 01:00:53.900
you

01:00:53.900 --> 01:00:54.400
you

01:00:54.400 --> 01:00:56.400
Thank you.

01:00:56.400 --> 01:01:26.380
Thank you.

