Data validation and conversion is one of the truly tricky parts of getting external data into your app.
This might come from a REST API or a file on disk or somewhere else.
It includes checking for required fields, the correct data types, converting from potentially compatible types,
for example, from strings to numbers if you have quote 7 but not the value 7, and much more.
Pydantic is one of the best ways to do this in modern Python,
using data class-like constructs and type annotations to make it all seamless and automatic.
We welcome Samuel Colvin, creator of Pydantic, to the show.
We'll dive into the history of Pydantic and its many uses and benefits.
This is Talk Python to Me, episode 331, recorded April 14th, 2021.
Welcome to Talk Python to Me, a weekly podcast on Python, the language, the libraries, the ecosystem, and the personalities.
This is your host, Michael Kennedy.
Follow me on Twitter where I'm @mkennedy,
and keep up with the show and listen to past episodes at talkpython.fm,
and follow the show on Twitter via at Talk Python.
This episode is brought to you by 45 Drives and us over at Talk Python Training.
Please check out what we're offering during those segments.
It really helps support the show.
Hey, Samuel.
Hi, Mike. Great to meet you. Very excited to be here.
Yeah, I'm really excited to have you here.
You've been working on one of my favorite projects these days that is just super, super neat,
solving a lot of problems, Pydantic, and I'm really excited to talk to you about it.
Yeah, as I say, it's great to be here.
I've been doing Pydantic on and off now since 2017,
but I guess it was when Sebastian Ramirez started using it on FastAPI a couple of years ago.
A couple of years ago, I don't know when, that it really went crazy.
And so now it's a lot of work, but it's exciting to see something this popular.
I'm sure it's a lot of work, but it really seems to have caught the imagination of people
and really excited about it.
So fantastic work.
Before we get into the details there, let's start with just your background.
How did you get into programming Python?
I did a bit of programming at university, a lot of, a bit of MATLAB and a bit of C++.
And then my first job after university, I worked on oil rigs in Indonesia, of all strange things.
There's a lot of time on an oil rig when you're flat out, and there's a lot of other time when
you're doing absolutely nothing, don't have much to do.
And so I programmed quite a lot in that time.
And I suppose that was when I really got into loving it rather than just doing it when I had to.
And then I guess Python from there, a bit of JavaScript, a bit of Python ever since.
Yeah, really cool.
I think things like MATLAB often do sort of pull people in and they have to learn a little bit
of programming because it's a pain to just keep typing it into whatever the equivalent of the
REPL is that MATLAB has.
You do the .imp file and you kind of get going and you start to combine them.
And then all of a sudden, it sort of sucks you into the programming world when you maybe
didn't plan to go that way.
I worry maybe other people learn it the right way and sit down and read a book and understand
how to do stuff.
But there's a lot of things that I wish I had known back then that I learned only through
reading other code or through banging my head against the wall that would have been really
easy to learn if someone had showed me them.
But hey, we got here.
Yeah, I think many, I'd probably say most people got into programming that way.
And I think it's all fine.
Yeah, it's all good.
So what was living on an oil rig like?
That must have been insane.
It was pretty peculiar.
So half the time I was on land rigs and half the time I was offshore.
Offshore, the conditions were a lot better.
Land rigs, the food was pretty horrific.
And a lot of they were all, I always did like a 14 hour night shift and then occasionally
had to do the day shift as well and then go on to the night shift.
So sleep was a problem and the heat was a problem.
Yeah, it was hard baptism of the real working world after university, I must say.
Yeah, I guess so.
It sounds really interesting.
Not an interesting like, wow, I really want to necessarily go out there and do it.
But you just must have come away with some like some wild stories and different perspective.
Yeah, lots of stories that I won't retell now.
I don't think that's the kind of subject matter you want on your podcast.
But yeah, I mean, how oil rigs work, well, whatever you think about it and however much
we all want to get away from them is crazy what you can do.
And so being like up against the at the coalface of that was really fascinating.
It's kind of like aerospace, but no one minds crashing.
So you can innovate.
You can try a new thing.
You just the faster you can drill, the better.
It's all that anyone cares about.
Yeah.
Well, most people what they care about.
Yeah, it must have been a cool adventure.
Awesome.
So I thought it'd be fun to start our conversation, not exactly about Pydantic, but just about this
larger story that larger space that Pydantic lives in.
But maybe to set the stage, give us like a real quick overview of what problem does Pydantic
solve for the world?
What is it and what does it solve for the world?
So the most simplest way of thinking about it is you've got some user somewhere.
They might be on the other end of a web connection.
They might be using a GUI.
They might be using a CLI.
It doesn't really matter.
And they're entering some data.
And you need to trust the data that you that they put in is correct.
So with Pydantic, I don't really care what someone enters.
All I care about is that you can validate it to get what you want.
So if I need an integer and a string and a list of bytes, all I care about is that I can
get to that integer, string, and list of bytes.
I don't care if someone entered bytes for the string field as long as I can decode it.
Or if they entered a tuple instead of a list.
But yeah, the fundamental problem is people, I suppose in theory, like intentionally trying
to do it wrong, but mostly innocently getting something wrong, trying to coerce data into
the shape that you want it to be in.
Yeah, it's super easy to say like, well, I read this value from a file and the value
in the file is a number.
It says one, but you read it as strings.
So the thing you sent over is quote one, not the number one.
And in programming, computers hate that.
They don't think those things are the same unless maybe your JavaScript and you do it in
the right order, maybe.
But excluding that odd case, right?
A lot of times it's just crash wrong data format or whatever.
We expected an integer and gave us a string.
But, you know, Pydantic just looks at it and says, it could be an integer if you want it
to be.
So we'll just do that.
Yeah.
And I think it's something that there's been a lot of discussion about on Pydantic's issue
tracker.
There's an entire label strict about the discussions about how strict we should be.
And I definitely, I think it's fair to say Pydantic compared to its, the other libraries
is more lenient, more keen on if we can try and coerce it into that type, we will.
That really started for me trying to make it simple and performant.
And I just called the, I decided if something wasn't in by calling int on it and seeing what
happened.
That had some kind of strange side cases because I called list on something to see if it was
a list.
And that meant you could put a string of integers, a string into a list of ints field.
And it would first of all, call list on it, turn it into a list and then call int on each
member.
And that was completely crazy.
So we've got stricter over the years.
And I think that in the future, Pydantic will have to get a bit stricter in particular stuff
like coercing from a float to an int quite often doesn't make sense or isn't what people
want.
But most of the time, just working is really powerful, really helpful.
If there's going to be some kind of data loss, right?
If it's 1.0000, coercing that to one's fine.
If it's 1.52, maybe not.
Yeah, but it's difficult because we're in the Python world where Python is pretty lenient.
So it doesn't mind you adding two floats together.
If you call the int function on a float, it's fine.
So it's also trying to be Pythonic at the same time as being strict enough for people, but
without doing stuff that obviously, without not doing stuff that's obvious, because lots
of people do just want coercion to work.
Right.
We'll get into it.
There's lots of places to plug in and write your own code and do those kinds of checks if
you really need to.
Yeah.
All right.
Carlos has a good question out there in the live stream, but I'm going to get to that later
when we get a little bit more into it.
First, let's talk about some of the other things.
So Pythonic is surprisingly popular these days, but there's plenty of people, I'm sure, who
haven't heard of it, who are hearing about it the first time here.
And there's other libraries that try to solve these same type of problems, right?
Problem is I've got data in often a dictionary list mix, right?
Like a list of dictionaries or a dictionary, which contains lists, which contains like that
kind of object graph type of thing.
And I want to turn that into probably a class, probably a Python class of some sort or understand
it in some way.
So, you know, we've got some of the really simple ways of doing this where I just have
those.
I want to stash that into a binary form, which is pickle.
Pickle has been bad in certain ways because it can run arbitrary Python code.
That's not ideal.
There's another related library called quickle, which is like pickle, but doesn't run arbitrary
code.
And that's pretty nice.
We have data classes that look very much like what you're doing with Pydantic.
We have Marshmallow, which I hear often being used with like Flask and SQLAlchemy and Marshmallow.
You may want to just sort of give your perspective on like what the choices are out there and where
things are learning from other libraries and growing from.
Yeah, I put serialization as in pickle, JSON, YAML, TOML, all of them into a different category
message pack as a slightly different thing from taking Python objects and trying to turn
them into classes.
So putting them to one side, because I think that's a kind of different problem that Pydantic
and Marshmallow and people aren't trying to solve exactly.
Then there's data classes, which are the kind of canonical standard library way of doing this.
They're great, but they don't provide any validation.
So you can add a type in that says age is an integer, but data classes don't care.
Whatever you put in will end up in that field.
And so that's useful if you have a fully type-inted system where you know already that something's
an integer before you pass it to a data class.
But if you're loading it from a foreign source, you often don't have that certainty.
And so that's where libraries like Pydantic and Marshmallow come in.
Actually, Pydantic has a wrapper for data classes.
So you basically import the Pydantic version of data classes instead of normal data classes.
And from there on, Pydantic will do all the validation, give you back a completely standard
data class.
Having done the validation.
Marshmallow is probably, well, it is undoubtedly the biggest, most obvious competitor to Pydantic.
And it's great.
I'm not going to sit here and bad mouth it.
It's been around for longer and it does a lot of things really well.
Pydantic has just overtaken a few months ago Marshmallow in terms of popularity, in terms
of GitHub stars.
Whether you care about that or not is another matter.
There's also Atras, which kind of predates data classes and is closer to data classes.
But the big difference between Pydantic and Marshmallow and most of the other competitors
is Pydantic uses type hints.
So that one means you don't have to learn a whole new kind of micro language to define
types.
You just write your classes and it works.
It works with mypy and with your static type analysis.
It works with your IDE, like, well, Pycharm now, because there's an amazing extension.
I forgot the name of the guy who wrote it, but there's an amazing extension that I used the
whole time with Pycharm that means it works seamlessly with Pydantic.
And there's some exciting stuff happening.
Microsoft, they emailed me actually two days ago.
One of their technical fellows about extending their language server or their front-end for
language server, PyWrite, to work with Pydantic and other such libraries.
So because you're using standard type hints, all the other stuff, including your brain, should,
in theory, click into place.
Yeah, that's really neat.
I do think it makes sense to separate sort of the serialization file, save me a file,
load a file type of thing out.
I really love the way that the type hints work in there because you can almost immediately
understand what's happening.
It's not like, oh, this is the way in which you describe the schema and the way you describe
the transformations.
It's just, here's a class.
It has some fields.
Those fields have types.
That's all you need to know.
And Pydantic will make the magic happen.
What would you say the big difference between Pydantic and Marshmallow is?
And I haven't used Marshmallow that frequently, so I don't know it super well.
I would first give the same proviso.
I haven't used it that much either.
I probably, if I was more disciplined, I'd have sat down and used it for some time before
building Pydantic.
But that's not always the way things work.
The main difference is it doesn't use type hints or it doesn't primarily use type hints
as its source of data about what type something is.
Pydantic is around, from my memory, I can check, but significantly more performant than Marshmallow.
Yeah, you actually have some benchmarks on the site and we can talk about that in a little
bit and compare those.
Yeah.
So just briefly, it's about two and a half times faster.
The advantage of Marshmallow at the moment is it has more logic around customizing how
you serialize types.
So when you're going back from a class to a dictionary or list of dictionaries and then
out to JSON or whatever, Marshmallow has some really cool tools there, which Pydantic doesn't
have yet.
And I'm hoping to build into V2 some more powerful ways of customizing serialization.
Okay.
Fantastic.
This portion of Talk Python is brought to you by 45 Drives.
45 Drives offers the only enterprise data storage servers powered by open source.
They build their solutions with off-the-shelf hardware and use software-defined open source
designs that are unmatched in price and flexibility.
The open source solutions 45 Drives uses are powerful, robust, and completely different.
And best of all, they come with zero software licensing fees and no vendor lock-in.
45 Drives offers servers ranging from four to 60 bays and can guide your organization through
any sized data storage challenge.
Check out what they have to offer over at talkpython.fm/45 Drives.
If you get in touch with them and say you heard about their offer from us, you'll get a chance
to win a custom front plate.
So visit talkpython.fm/45 Drives or just click the link in your podcast player.
Let's dive into it.
And I want to talk about some of the core features here.
Maybe we could start with just you walking us through a simple example of creating a class
and then taking some data and parsing over.
And you've got this nice example right here on the homepage.
I think this is so good to just sort of look.
There's a bunch of little nuances to cool things that happen here that I think people will benefit from.
Yeah, so you're obviously defining your class user here.
Very simple inheritance from base model, no decorator.
I thought about the beginning that like this should work for people who haven't been writing
Python for the last 10 years and where decorators look like strange magic.
I think using inheritance is the obvious way to do it.
And then obviously we define our fields.
The key thing really is that the type int in the case of ID is used to define what type that
field is going to be.
And then if we do give it a value as we do with name, that means that the field is not required.
It has a default value.
And obviously we can infer the type there from the default, which is a string.
Then sign up timestamp is obviously an optional date time.
So it can be none.
And critically here, you could either enter none or leave it blank.
And it would again be none.
And then we have friends, which is a more complex type.
That's a list of integers.
And the cool stuff is because we're just using Python type int, we can burrow down into lists
of dicks, of lists, of sets, of whatever you like within reason.
And it will all continue to work.
And then looking at the external data, again, we see a few things like we were talking about
the coercion.
Right.
This external data is just a dictionary that you probably have gotten from an API call,
but it could have come from anywhere.
It doesn't have to come from there.
Right.
Exactly.
Anywhere outside.
But right now we've got it as far as being a dictionary.
So the point here is we're doing a bit of coercion.
So trivial converting from a string 123 to the number 123, but then a bit more complex,
parsing the date and converting that into a date object.
Right.
So you have the, in here, the data that's passed in, you've got a quote, 2019-06-01 and
a time.
And this is notoriously tricky because things like JSON don't even support dates.
Like, they'll freak out if you try to send one over.
So you just got this string, but it'll be turned into a date.
Yeah.
And we do a whole bunch of different things to try and do all of the sensible date formats.
There's obviously a limit as how far to go, because one of the things Pedantic can do is
it can interpret integers as dates using Unix timestamps.
And if they're over some threshold in about two centuries from now, it assumes there are
milliseconds.
So it works with Unix milliseconds, which are often used, but it does also lead to confusions
when someone puts in one, two, three as a date and it's three seconds after 1970.
There are like, there's an ongoing debate about exactly like what you should try and coerce
and when it gets magic.
But for me, it's, there's a number of times I've just found it incredibly useful to, that
it just works.
So for example, the string format that Postgre uses when you use to JSON just works with Pedantic.
So you don't even have to think about whether that's come through as a date or as a string
until you're worried about a limit of performance.
Most of that stuff just works.
Yeah.
Then one of the things that I think is super interesting is you have these friend IDs that
you're passing over.
And you said in Pedantic, it gets a list of integers.
And in the external data, it is a list of sometimes integers and sometimes strings.
But once it gets parsed across, it not just looks at the immediate fields, but it looks at,
say, things inside of a list and says, oh, you wanted a list of integers here.
This, this list, it has a string in it, but it's like quote three.
So it's fine.
Yeah.
And this is where, where it gets cool because we can go recursively down into the rabbit hole
and it will continue to validate.
One of the tricky things that I think is what most people want, but where our language fails us is about the word validation.
Because quite often validation sounds like I'm checking the input data is in the form that I said.
That's kind of not what Pedantic is doing.
It's optimistically trying to parse.
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.
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,
because it's going to accept a million different things.
It's going to be as lenient as possible in what it will take in.
But that's by design.
Yeah.
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.
So you say user of star star dictionary.
So that'll, you know, explode that out to keyword arguments.
And that's it.
That runs the entire parsing, right?
That's super simple.
Yeah.
And that's, again, by design to make it just the simplest way of doing it.
If you want to do crazy complex stuff like constructing models without doing validation,
because you know it's already been validated, that's all possible.
But the simplest interface, just calling in it on the class is designed to work and do the validation.
Yeah, cool.
One thing I think is really neat and not obvious right away is that you can nest these things as well, right?
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?
Exactly.
And it's probably an open question as to how complex we should make this first example.
Maybe it's already too complicated.
Maybe it doesn't demonstrate all the power.
But yeah, I think it's probably about right.
But yeah, you can go recursive.
You can even do some crazy things like the root type of a model can actually not itself
be a sequence of fields.
It can be a list itself.
So there is a long tail of complex stuff you can do.
But you're right.
The inheritance of different models is a really powerful way of defining types.
Because in reality, our models are never a nice key in value of different types.
They always have complex items deeper down.
Right.
That's really cool.
And on top of these, I guess we can add validation.
Like you have the, you said, the very optimistic validation.
Like if in the friends list, it said one, two, comma, Jane.
Well, it's probably going to crash when it tries to convert Jane into an integer and say, no, no, no,
this is wrong.
And it gives you a really good error message.
It says something like the third item in this list is Jane.
And that's not an integer, right?
It doesn't just go, well, data is bad.
You know, too bad for you.
Yeah.
But you could decide, perhaps Jane's a strange case.
But if you wanted the word one, O-N-E, to somehow work, you could add your own logic relatively
trivially via a decorator, which said, okay, cool.
If we get the word one or the word two or the word three, then we can convert them to the equivalent
integers.
And that's relatively easy to add.
Yeah.
Cool.
Now, one thing that I guess the scenario where people run into this often run into working
with Pydantic is it's sort of the exchange layer for FastAPI, which is one of the more
popular API frameworks these days.
And you just say your API function takes some Pydantic model and it returns some Pydantic model
and, you know, magic happens.
But if you're not working in that world, if you're not in FastAPI and most people doing
web development these days are not because, you know, it's the most popular framework and
it's also certainly not the most popular legacy framework.
So you can do things like create these models directly in your code, let it do the validation
there.
And then if you need to return the whole object graph as a dictionary, you can just say model.dict,
right?
Yeah.
And also model.json and that will take it right out to JSON.
As a string.
Yeah.
So it will turn a string of that JSON.
Yeah.
And you can even swap in more performant JSON encoders and decoders.
And this is why I was talking about like there's already some power in what you can do here and
which fields you can exclude.
And it's pretty powerful.
But like, as I say, something in V2 that we might look at to make even more powerful is customizing
how you do this.
Nice.
You might expect to just call .dict and it converts it to a dictionary and reverses it,
if you will.
But there's actually a lot of parameters and arguments you can pass in to have more control.
Do you want to talk about maybe just some of the use cases there?
Yeah.
As we see here, you can choose to include specific fields and the rest will be excluded
by default.
You can say exclude, which will exclude certain fields and the rest will be included by default.
Those include and exclude fields can do some pretty ridiculously crazy logic going recursively
into the models that you're looking at and excluding specific fields from those models or specific
items from lists, even some of the code.
I forget who wrote that, but like I wrote the first version of it.
I wrote the second or third version of it.
And I was looking through about the 10th version of it the other day.
And it is some of the most complex bits of Pydantic.
But that's amazingly powerful.
And then we didn't talk about aliases, but you can imagine if you were interacting with the
JavaScript framework, you might be using camel case on the front end for like user names,
name with a capital N, but have user underscore name in Python world where we're using underscores.
We can manage that by setting aliases on each field.
So saying that the user underscore name field has the alias user underscore N name.
And then we can obviously export to a dictionary using those aliases if we wish.
Oh, that's cool.
So you can program Pydonic style classes, even if you're consuming, say, a Java or C#
API that clearly uses a different format or style for its naming.
Exactly.
And you can then export again to back to those aliases when you want to go on to like pipe
it on down your data processing flow or however you're going to do it.
And in fact, one of the things that will come up in future will be different aliases for import
on the inside way and the out way.
But until so far, there's just one, but that's powerful.
And then you can exclude fields that have defaults.
So if you're trying to save that data to a database and you don't want to save more than
you need to, you can exclude stuff where it has the default value.
And you can exclude fields which are none, which again is often the same thing, but there
are subtle cases where those are different requirements.
Yeah.
And that also might really just, even if you're not saving it to a database, you know, that
will lower the size of the JSON traffic between say microservices or something like that.
If you don't need to send the defaults over, especially the nones, because they're probably
going to go to the thing they get and go, give me the value or none in whatever language
they're using, right?
Something to that effect.
It'll mean the same.
Yeah.
So we have complex stuff here.
So we have exclude unset and exclude defaults.
So you can decide exactly how you want to exclude it to get just the fields that are actually live,
as it were, that have custom values.
Yeah.
So you have this dict thing, which will turn a dictionary and then you have the JSON one,
which is interesting.
We talked about that one.
It also, you also have copy.
So you, is that like a shallow copy if you want to clone it off or something?
It can be a deep copy, but you can update some fields on the way through.
So there are multiple different contexts where you might want to do that, where you want
different models where you can go and edit one of them and not damage the other one,
or you want to modify a model as you're copying it.
We also have a setting on config that prevents you, that makes fields pseudo immutable.
So it doesn't allow you to change that value.
It doesn't stop you changing stuff within that value because there's no way of doing that in Python.
But that's another case where you might want to use copy because you've set your model up to be
effectively static and you want to create a new one.
If you were being super careful and strict, you would say, I'm never going to modify my model.
I'm just going to copy it when I want a new one.
Yeah.
Perfect.
Yeah.
Certain things you could enforce that they don't change like strings and numbers.
But if it's a list, like the list can't point somewhere else, but what's in the list,
you can't really do anything about that, right?
Yeah.
So another thing that I think is worth touching on that's interesting is there's some obvious
things that are the types that can be set for the field types.
And then the data exchange conversion types, bools, integers, floats, strings, and so on.
But then it gets further, it gets more specialized down there as you go.
So for example, you can have unions, you can have frozen sets, you can have iterables,
callables, network addresses, all kinds of stuff, right?
Yeah.
We try and support almost anything you can think of from the Python standard library.
And then if you go on down, we get to a whole bunch of things that aren't supported
or don't have an obvious equivalent within the standard library, but where you go on
down to Pydantic types on the right.
So where you're looking is in the example, but Pydantic types, then we get into things
that don't exist within, or there isn't an obvious type for in Pydantic, in Python.
There's a kind of subtle point here that like often these types are used to enforce more
validation.
So what's returned is an existing Python type.
So here we have file path, which returns a path, but it just guarantees that that file
exists.
And directory path, similarly, is just a path, but it will confirm that it is a directory.
And email string just returns you a string, but it's validated that it's a legitimate email
address.
But then some do more complex stuff.
So name email will return you, allow you to split out names and email addresses and more
complex things.
Got credit cards, colors, URLs, all sorts of good stuff there.
Yeah.
Yes.
So there's a Mexican bank called, excuse my pronunciation, Cavenka, who use the credit card
fields for all of their validation of credit card numbers.
Yeah.
It's super interesting.
And you think about how do you do this validation?
How do you do these conversions yourself?
And not only do you have to figure that out, but then you've got to do it in the context of
like a larger data conversion.
And here you just say, this field is a type email and it either is an email or it's going
to tell you that it's invalid.
Right.
Yeah.
I like this a lot.
You know, one thing that we've been talking about that's pretty straightforward is using
this for APIs, right?
Like somebody is doing a JSON post and you get that as a dictionary in your web framework,
and then you kind of validate it and convert it and so on.
But it seems like you could even use this for like web forms and other type of things,
right?
Somebody's submitting some kind of ATP post of a form and it comes over if you want to say
these values are required, this one's an email and so on.
Yeah.
So all of the form validation for multiple different projects I've built, some open source, lots
proprietary use Pydantic for form validation and use the error messages directly back to the
user.
I've got quite a lot of JavaScript that basically works with Pydantic to build forms and do validation
and return that validation to the user.
I've never quite gone far enough to really open source that and push it as a React plugin
or something, but it wouldn't be so hard to do.
So let me ask you this.
And maybe when you say React, maybe that's enough of an answer already.
But if it's a server side processing form and it's not a single page sort of front and framework
style of form submission, often what you need to do is return back to them the wrong data,
right?
If they type in something that's not an email and over there it's not a number, you still
want to leave the form filled with that bad email and that thing that's not a number.
So you can say, those are wrong.
Keep on typing them.
Is there a way to make that kind of round tripping work with Pydantic?
I've tried and haven't been able to do it.
But if it's a front end framework, it's easy to say, submit this form, catch the error and
show the error because you're not actually leaving the page or clearing the form with a
reload.
Yeah, the short answer is no.
And it's something there's an issue about for V2 to return like the wrong value always
in the context of each error.
Often you kind of need it to make the error make sense.
But at the moment, it's generally not available and we will add it in V2.
What I've done in React is obviously you've still got the values that went into on the
form.
So you don't clear any of the inputs.
You just add the errors to those fields and set the whatever it is, invalid header.
So they're nice and red.
Yeah, exactly.
So if it was a front end framework like React or Vue, I can see this working perfectly as
you try to submit it for a form.
But if you got to round trip it with like a Flask, a Pyramid or Django form or something
that doesn't have it, then the fact that it doesn't capture the data.
I mean, I guess you could return back just the original posted dictionary, but yeah.
Yeah.
So you have the original.
Yeah.
Like you say, you've normally normally there.
It's not too many different levels and it's relatively simple to combine up the dictionary
you got straight off form submission with the errors.
But yeah, it's you're right.
It's something we should improve.
Yeah.
I'm not sure that you necessarily need to improve it because it it's mission is being fulfilled
exactly how it is.
And if it has this time, but well, sometimes it throws errors and sometimes it doesn't.
And then like just report, I don't know, it seems like you could overly complicate it as
well.
I think that there's like a really difficult challenge in tragedy of the commons in like
someone wants a niche feature.
Someone else wants that feature.
You get 10 people wanting that feature.
You feel under overwhelming pressure to implement that feature.
But you forget that there's, I forget, you know, there's like 6,000 people who started,
but it's like 10, 12,000 projects that use Pydantic.
Those people haven't asked for that.
Do they want it or would they actively prefer that Pydantic was simpler, faster, smaller?
Exactly.
Right.
Part of the beauty of it is it's so simple, right?
I do.
I get the value.
I define the class.
I star, star, take the data.
And it's either good or it's crashed, right?
If it gets past that line, you should be happy.
Yeah.
And I think that like I'm pretty determined to keep that stuff that simple.
There are those who want to change it, who say initializing the class shouldn't do the
validation, then you should call a validate method.
I'm not into that at all.
There's stuff where I'm definitely going to keep it simple and there's stuff where I'm
really happy to add more things.
So we were talking about custom types before.
I'm really happy to add virtually, not virtually any, but like a lot of custom types when someone
wants it, because if you don't need it, it just sits there and mostly doesn't affect
people.
Right.
If you don't specify a column or a field of that type, it doesn't matter.
You'll never know it or care.
Yeah.
So there's a couple of comments in the live stream.
And I think maybe we can go ahead and touch on them about sort of future plans.
So you did mention that there's going to be kind of a major upgrade of V2.
And Carlos out there asks, is there any plans for Pydantic to give support for PySpark data
frame scheme validation?
Or, you know, let me ask more broadly, like any of the data science world integration with
like Pandas or other, you know, NumPy or other things like that?
There's been a lot of issues on NumPy arrays and like validating them using list types without
going all the way to a like Python list because that can have performance problems.
I can't remember because it was a long time ago, but people, whoever it was, found a solution.
And like Pydantic is used a lot now in data science.
If you look at the projects it's used in by Uber and by Facebook, they're like big machine
learning projects.
Fast Facebook's fast MRI library uses it.
Like it's used a reasonable amount in like big data validation pipelines.
So I don't know about PySpark.
So I'm not going to be able to give a definitive answer for that.
If you create an issue, I'll endeavor to remember to look at it and have a look and give an answer.
But you'll start your PySpark research project.
Yeah.
Nice.
Also from Carlos really is like, what's the timeframe for V2?
Someone joked to me the other day that the release date was originally put down as the end of March
2020 and that didn't get reached.
And it's still, the short answer is that like, I need to, there are two problems.
One is I need to set some time aside to sit there and build quite a lot of code.
Second problem is the number of open PRs and the number of issues.
I find it hard sometimes to bring myself to go and work on Pydantic when I have time off,
because a lot of it is like the trawl of going through issues and reviewing pull requests.
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.
Because I do that for a day job quite a lot.
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.
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.
But I think that that's better.
Okay.
Yeah.
Well, and also in your defense, a lot of things were planned for March 2020.
Yeah.
And got ready.
That is true.
I have sat at my desk in my office for a total of about eight hours since then.
Yeah.
So I haven't been back to the office in London at all.
So, so yeah, I would hope this year.
Yeah.
Cool.
Talk Python to me is partially supported by our training courses.
Do you want to learn Python, but you can't bear to subscribe to yet another service?
At Talk Python Training, we hate subscriptions too.
That's why our course bundle gives you full access to the entire library of courses for one fair price.
That's right.
With the course bundle, you save 70% off the full price of our courses and you own them all forever.
That includes courses published at the time of the purchase, as well as courses released within about a year of the bundle.
So stop subscribing and start learning at talkpython.fm/everything.
And then related to that, a risky chance asks, where should people who want to contribute to PyDentix start?
And I would help you kick off this conversation by just pointing out that you have tagged a bunch of issues as help wanted.
And then also maybe reviewing PRs, but what else would you add to that?
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.
Like triage type stuff?
Yeah, but if you go on to discussions, we use the GitHub discussions, which maybe people don't even see.
But these are all questions you can go in and answer if someone has a problem.
Lots of them aren't that complicated.
I know that's perhaps not what risky chance meant in terms of writing code.
And that's obviously, for some of us, where the fun lives.
But these questions would be enormously helpful if people could help.
You can see some of them are answered, and that's great.
But there are others that aren't.
And then, yeah, reviewing pull requests would be the second most useful thing that people could do.
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.
And then I do love submissions.
I noticed today there were 200 and something people who've contributed to Pydantic.
So I do do my best to support anyone who comes along, however inexperienced or experienced building features or fixing bugs.
Yeah, fantastic.
Another thing I want to talk to you about, is this the right one?
I believe.
No, the validating decorator.
Well, let's talk about validators first.
We touched on this a little bit.
So one thing you can do is you can write functions that you decorate with this validator decorator.
And it says, this is the function whose job is to do a deeper check on a field, right?
So you can say this is a validator for name, this is a validator for username or validator for email or whatever.
And those functions are ways in which you can take better control over what is a valid value and stuff like that, right?
Yeah, but you can do more.
You can't just be stricter, as in raising error, if it's not how you want it.
You can also change the value that you're going to that's come in.
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.
So capitalize the first letter.
So you can also change the value you're going to put in.
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.
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.
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?
Yeah. And there's allow mutation, which you've got to there, which is super helpful.
That's where we can stop fields from being modified.
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?
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.
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?
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.
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.
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?
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.
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?
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.
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.
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.
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.
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.
Yeah. And that sounds like more work than just calling the function for sure. It depends on how much it does, right?
Does it cache that kind of, does it like cache the class that it creates when it decorates a function?
It caches the model.
Yeah.
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.
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.
Maybe it might make sense to put this on the boundary entry point type of thing, but nowhere else.
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.
Or none type has no attribute such and such.
Yeah.
Whatever.
Right.
That standard error they always run into.
Okay.
That's pretty interesting.
Let's talk a little bit about speed.
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.
So we've got Pydantic, we've got Adders, we've got Valadeer, which I've never heard about, but very cool.
Marshmallow and a couple of others like Django REST Framework and Cerebus.
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?
Yeah.
And the first thing I'll say is that there were lies, damn lies and benchmarks.
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.
Short of writing your own custom code in each place to be like, yeah, to do manual validation, which is a massive pain.
And if you're doing that, you probably want to go and write it in a proper compiled language anyway.
Right.
Or maybe just use Cython on some little section, something like that, right?
So all of Pydantic is compiled with Cython and is about twice as fast.
If you install it with pip, you will get mostly the compiled version.
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.
So it's already faster than just calling Python.
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.
Yeah.
Fantastic.
Okay.
I didn't realize it was compiled with Cython.
That's great.
Yeah.
It's part of the magic, huh?
Making it faster.
Yeah.
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.
It's Python compiled with Cython rather than real Cython code.
So it's not as C speed, but it's faster than just calling Python.
Yeah, absolutely.
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.
Otherwise, I think it's an open question.
I think it's an open question whether Cython is faster with type hints.
It does.
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.
But yeah, I think it does use it in places.
Yeah.
I was thinking more like you don't have to rewrite it in Cython.
I don't have to convert Python code to Cython code where it has its own sort of descriptor language.
But like if you have Python code that's type annotated, it'll take that and run with it these days.
I think it isn't any faster or any better because of the type hints much.
Although someone out there is an expert and I don't want to say that.
So I'm not sure.
Yeah, I hear you.
All right.
Another thing I want to touch on is the data model code generator.
You want to tell us about this thing?
What is this?
I haven't used it much.
But yes, what we haven't talked about here.
It's just what it is.
Is JSON Schema, which is what Sebastian Ramirez implemented a couple of years ago when he was first starting out on FastAPI.
And it's one of the coolest features of FastAPI.
And Pydantic is that once you've created your model, you don't just get model and model validation.
You also get a schema generated for your model.
And in FastAPI, that's automatically created with Redock into really smart documentation.
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.
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.
And data model code generation, as I understand it, is generating those JSON schema schemas for models.
Is that right?
Yeah, I think so.
It feels to me like it's the reverse of what you described from what Sebastian has created, right?
Like given one of these open API definitions, it will generate the Pydantic model for you.
So if I was going to consume an API and I'm like, well, I got to write some Pydantic models to match it.
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.
Yeah.
I had it around the wrong way.
But yeah, my instinct is I haven't used it, but that it gets you, it does 90% of the work for you.
And then there's a bit of like manual tinkering around the edge to change some of the types I suspect.
But like, yeah, really useful.
Yeah.
And it supports different frameworks and stuff.
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.
So for example, I built this weather, real time, weather, live weather data service for one of my classes over at weather.talkpython.fm.
I built that in FastAPI and it exchanges Pydantic models.
And all you got to do in order to see the documentation, just go to slash docs.
And then it gives you the JSON schema.
So presumably I could point that thing at this and then it would generate.
And go back to the model.
Exactly.
And get a fairly complicated Pydantic model prebuilt for me, which I think is pretty excellent.
Yeah.
It's worth saying maybe you disagree, but I think the redock version of the documentation or autodox is even smarter than that one.
I don't know if you've got it.
Yeah, that one I think is even smarter.
Oh yeah, this is a really nice one.
I like this one a lot.
Yeah, it even gives you the responses there.
It could be 200 or 422, which I did build that into there, but I didn't expect it to actually know.
That's pretty interesting.
Yeah, it's cool.
Yeah, it's very cool.
So they're both there, either slash docs or slash redock.
FastAPI will pull them.
You can switch one off or change the endpoints, but yeah.
Yeah.
And by the way, if you're putting out a FastAPI and you don't want public documentation,
make sure that you set docs, the docs URL and the redox URL to none.
And when you're creating your app or your API instance.
So yeah, that's always on unless you take action.
So you better be sure you want to.
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.
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.
But it's a bit of a weird thing to make public, even if there's nothing particularly sensitive.
So yeah, you can put it behind authentication.
Yeah.
Yeah, very good.
All right.
You already talked about the PyCharm plugin, but maybe give us a sense for why do we need a PyCharm plugin?
I have PyCharm.
And if it has the type information, a lot of times it seems like it's already good to go.
So what do I get from this PyCharm plugin?
Like, why should I go put this in?
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.
And PyCharm will correctly give us information about, we'll say, okay, first name exists, like foobar name doesn't exist.
It's a string, so it makes sense to add it to another string.
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.
I see.
It looks like star, star, KWR, good luck, go read the docs.
Yeah, exactly that.
But this is where the PyCharm plugin comes in because it gives you documentation on the arguments.
Okay, so it looks at the fields and their types and says, well, these are actually keyword arguments to the constructor, the initializer.
Yeah.
Okay.
Yeah, got it.
That's very cool.
And it will also, I don't even know what it does.
I just use it the whole time and it works.
You know those things, but you don't even think about them.
Yeah, cool.
So it gives you auto-completion and type checking, which is cool for the initializer, right?
So if you were to try to pass in something wrong, it lets you know.
Also, it says it supports refactoring.
If you refactor the keyword.
One of the really useful things it does is when we talked about validators, which are done by a decorator, they are class methods.
It's very specifically because you might think that they're instance methods and you have access to self.
You don't because they're called before the model itself is initialized.
So the first argument to them should be class CLS.
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.
Oh, interesting.
Okay.
Yeah, that makes a lot of sense.
Because it's converting and checking all the values and then it creates the object and assigns the fields, right?
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.
Yeah, cool.
And Risky Chance loves that it works with aliases too, which is pretty cool.
Oh, yeah.
It does.
It does lots of cool things.
I'm really impressed by it.
It's one of the coolest things that come out of Pydantic.
Awesome.
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.
So, yeah, that's...
We're also used to PyCharm just working on so many things that you don't even notice.
Like, yeah, you only notice when it doesn't work, so...
Yeah, absolutely.
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.
Yeah.
What are these?
You are also the author of Python DevTools, yeah?
Yeah, what is this?
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.
And I use it the whole time in development instead of print.
And obviously, I wanted it to show me my Pydantic models in a pretty way, so it has integration.
There are some hooks in DevTools that allow it to customize the way stuff's printed.
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.
So, Pydantic will also print pretty with Rich as well as with DevTools.
Yeah, cool.
Okay, really nice.
Yeah, Rich is a great TUI, Terminal User Interface Library for Python.
Yeah, it's cool.
It's different from DevTools.
I wouldn't say they compete.
DevTools is, for me, it's just...
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.
Nice.
So, what's the Pydantic plugin here, or connection, rather, here?
So, if I debug out of DevTools a model, I get just a really nice representation?
Yeah, exactly that.
It's not showing it...
It's because you're in the DevTools docs.
There's some other docs in Pydantic to give you an example.
But it'll give you a nice example if it expanded out rather than squashed into one line.
So, usage with DevTools is the last...
There you go.
Got it.
Yeah, so you see that.
The user picked out nicely instead of, yeah, done like that.
I suppose that's kind of...
That demonstrates its usage for me.
Yeah, perfect.
That looks really good.
It's nice to be able to just print out these sorts of things and see them really quickly.
What's the just basic string representation of a Pydantic model?
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...
String, str representation.
It's right there.
I think that's the string representation you're looking at right there.
Yeah, perfect.
So, you get a really rich sort of view of it embedded in the editor or if you print it...
And if you use Repra, then you get basically wrapped in user.
All right.
Okay.
So, it gives you...
As if it were...
Yeah.
You're trying to construct it out of that data.
Yeah.
Okay.
Fantastic.
Well, you know, we've covered a bunch of things and I know there's a lot more.
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.
But it's worth emphasizing that this is not just a FastAPI validation data exchange thing.
It works really great.
A lot of the stuff happens there.
But if you're using Flask, if you're using Pyramid, if you're using...
I don't know about Django so much because of the models and stuff there, but...
There is.
Someone's built Django admin.
So, one of the things we haven't talked about as well is settings management, which Pydantic has some pretty powerful features for.
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.
So, not just loading them from environment variables and from .m files, but also from Docker secrets.
Now we have an interface to load them from kind of anywhere.
So, you can build your own interface for loading settings from places.
But someone's built a Django settings tool with Pydantic to kind of validate your Django settings using Pydantic.
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.
Well, that have to be used with each other.
It fits with Pydantic, but it's used in lots of other big projects.
Or you can just use it in Flask or in Django or wherever you like.
Right.
If you're reading JSON files off a disk, it could totally make sense to use it.
Or you're doing screen scraping, potentially it makes sense.
Or just calling API, but you're the client of that API.
It could totally make sense to do that.
Yeah.
I just want to point out, like it's super broadly applicable, not just where people see it being really used.
And Nick H out there is definitely going to try this with Django.
So, awesome.
That's cool.
Yeah.
So, let's wrap this up with just, I know we spoke a little bit about V2 and the timing.
Like, what are the major features that you think, like what are the highlights that people should look forward to or be excited about?
There's a bigger problem at hand, which is the Python 3.10 at the moment in PEP.
I'm going to try and remind myself of the exact number.
But like in PEP 6.9, no, in 5.6.3, basically all type hints become strings instead of Python objects.
And so, and like that's been available in future.
Right.
Is that the lazy evaluation of the annotation, something like that?
But it's not even a lazy evaluation.
It's a non-evaluation.
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.
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.
And I'm going to try and put this forward.
But like I had a conversation today just before I came online now with someone who's created a PEP that should fix this.
But the current response from the core developers is to refuse it.
So I'm like really worried and frustrated that might happen.
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.
So we'll see what happens.
And normally with open source, people find a way around.
But like I think that's really worrying.
And I'll create an issue on Pydantic to track this properly.
But it is something to be aware of.
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.
It's important sometimes to notice before they're released and make a point.
Wow.
Well, I'm really glad you pointed that out.
I had no idea.
I mean, I knew there were minor behind the scenes changes from a consumer perspective of type annotations.
But that sounds like there's more going on for libraries like this.
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.
But if anyone's looking into it, I will create an issue on Pydantic to talk about this.
But something like this needs to happen.
Also, Larry emailed me an hour or two hours ago to talk about this.
But this is a really big problem that we need to like prevent like breaking lots of cool stuff that's happening in Python.
All right.
Well, I agree.
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.
It's a really fantastic direction and really building on top of the type annotation world.
And I would hate to see that get squashed.
What's incredible about it just briefly is that it's used by Microsoft in core bits of Office.
It's used by Uber, by the NSA, by like banks.
It's used by JP Morgan.
But it's also really easy to get started with at the very beginning.
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.
Not this idea that it has to be like dense and mainframe and impossible or like Mickey Mouse and not worth using.
Right.
Like FastAPI and Pydantic seem to be managing to be both.
I agree.
I think they are absolutely.
So, well, congratulations on building something amazing.
Thank you very much, Michael.
And hopefully, Pep 649 keeps things rolling smooth.
Hopefully that gets ironed out.
All right.
Now, we're pretty much out of time.
But before I let you out of here, let me ask the final two questions.
So if you're going to write some code, if you're going to work on Pydantic, what editor do you use?
I use PyCharm.
Right on.
And the Pydantic plugin, I'm guessing.
And the Pydantic plugin, yeah.
Right on.
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.
You should know about it.
I should probably not break the rule and talk about my own.
But like DevTools, which I talked about, is incredibly useful to me.
And so I would spread the word a bit on that.
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.
So from Coverage to Starlit, which is the other library that's the basis of FastAPI.
Sebastian's great.
And I mean, no offense to him, but he stands on the shoulders of people who've done lots of other things.
And they're really, really powerful.
So I would spare a bit of time for them.
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.
Yeah. And I'm going to go with Vlad out there.
He says, you need us know about PyTonic because of FastAPI.
I agree.
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.
But they're there because of...
But I should say again, FastAPI is awesome.
I didn't use it initially.
I'm a contributor to AIO HTTP, which is also really cool.
But I've, over the last year, become a complete convert to FastAPI.
I use it.
It's my go-to tool now.
So it's awesome.
Yeah.
Fantastic.
All right.
Final call to action.
People want to check out PyTonic.
Maybe they want to contribute to PyTonic.
What do you tell them?
Go and have a read through the docs.
And yeah, go from there.
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.
That would be...
That's awesome.
Cool.
All right.
And if I'm not there immediately and I don't reply for weeks, I'm sorry.
And I promise to as soon as I can.
Fantastic.
All right.
Samuel, thanks for being on the show.
It's been great to learn more deep information about PyTonic because it's so simple to use it.
It's easy to just skim the surface.
Awesome, Michael.
Thank you very much.
Yeah, you bet.
Bye-bye.
Yes, bye.
This has been another episode of Talk Python to Me.
Our guest in this episode was Samuel Colvin.
And it's been brought to you by 45 Drives and us over at Talk Python Training.
Solve your storage challenges with hardware powered by open source.
Check out 45 Drives storage servers at talkpython.fm/45 Drives and skip the vendor lock-in and software licensing fees.
Want to level up your Python?
We have one of the largest catalogs of Python video courses over at Talk Python.
Our content ranges from true beginners to deeply advanced topics like memory and async.
And best of all, there's not a subscription in sight.
Check it out for yourself at training.talkpython.fm.
Be sure to subscribe to the show.
Open your favorite podcast app and search for Python.
We should be right at the top.
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.
We're live streaming most of our recordings these days.
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.
This is your host, Michael Kennedy.
Thanks so much for listening.
I really appreciate it.
Now get out there and write some Python code.
Thank you.
Bye.
you
Thank you.