Pydantic has become a core building block for many Python projects.
After five years, it's time for a remake.
With version 2, the plan is to rebuild the internals,
with benchmarks already showing a 17 times performance improvement,
and cleanup of the API.
This sounds great, but what does it mean for us?
Well, Samuel Colvin, the creator of Pydantic,
is here to share his plan for Pydantic version 2.
This is Talk Python to Me, episode 376,
recorded August 4th, 2022.
Welcome to Talk Python to Me, a weekly podcast on Python.
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 talkpython.
We've started streaming most of our episodes live on YouTube.
Subscribe to our YouTube channel over at talkpython.fm/youtube
to get notified about upcoming shows and be part of that episode.
This episode of Talk Python to Me is brought to you by Compiler from Red Hat.
Listen to an episode of their podcast to demystify the tech industry over at talkpython.fm/compiler.
And it's brought to you by Microsoft for Startups Founders Hub.
Get early stage support for your startup and build that startup you've been dreaming about.
Visit talkpython.fm/foundershub to apply for free.
Transcripts for this and all of our episodes are brought to you by Assembly AI.
Do you need a great automatic speech-to-text API?
Get human-level accuracy in just a few lines of code.
Visit talkpython.fm/assemblyai.
Samuel, welcome back to Talk Python to Me.
It's great to be back.
It was, when was it?
It was in the middle of, it was in COVID, wasn't it?
I seem to remember.
It was core COVID, yes.
It was just 15 months ago.
Yeah.
Yeah, I think it was sometime last year.
Yeah, I was in my attic in the, yeah, I'm now in the office.
It's a representative.
Exactly.
Locked down in the house.
And it's great to have you back.
We talked about Pydantic back then.
Obviously, we're talking about Pydantic now as well.
I would say it's grown tremendously since then.
It was already quite popular then.
Yeah, I think it's, I don't have right now off the top of my head, good metrics on, you
know, insofar as you can quantify the growth of these things.
I think it, yeah, it's grown a lot, but I think the feeling for me is it's become a lot of,
a lot more companies and a lot more people have started to rely on it.
And it's become a kind of core tool that they expect to work in the way you expect pytest
or Django to work.
Not quite perhaps at those levels, but moving in that direction.
And yeah, I guess I'm probably jumping the gun, but at the beginning of this year, I was
thinking about it and I was obviously super proud of what, of how many people were using
Pydantic and how useful it was being, but I wasn't quite so proud of its internals, which
is why I started thinking about what it would look like to, to kind of start again.
Cause obviously V2 was an opportunity to, to break stuff.
Not that we haven't broken things in minor releases when we shouldn't have done, but like to formally
break things and do it right where it was obviously, I guess, wrong from the beginning.
The goal I'm sure is not to go out and break things, but sometimes in order to take years
of, of learning and experience and usage and turn that into the way you think it should
be, some things may have to break, right?
Yeah. I think that when I first released Pydantic, it wasn't, I've subsequently built projects.
I thought we're going to be really popular and there's be, you know, varied in their success,
but I literally built Pydantic for me and put it on, you know, put it on PyPy and then put it on
Hacker News to see what would happen. But because of that, I thought about the work, there was some
esoteric design decisions that were the stuff I wanted, but in reflect on reflection, they're not
right for a popular library used by lots of people. strictness being, I guess, the most obvious
example, but a bunch of other stuff. talk about strictness. We'll talk about a lot of these
changes, but why do you think it was popular? I think it came along at the right time.
I think it came along when tight pins were just getting popular in Python. They had been around
in some guys for like ever, right? You could do something with them in two seven, but they were
just beginning to become a thing. my pie was coming out, but I suppose I was not the only person
who, who was frustrated by the idea that they didn't have teeth that they were there, but,
but it seemed kind of weird, right? If you came from a, a rust or a C++ or a C background,
you know, types are everything. And the idea that they were there, but they meant nothing
was a bit of an anathema to me. And I just started off with a, can I, can I make them work a bit? And
that was five years ago and here we are.
Yeah. I agree that coming along at the right time was probably part of the magic. I think
there was just some, some libraries and some frameworks who decided these types should have
meaning. Like you said, there was a couple of web frameworks, obviously most notably FastAPI,
but there were other ones as well who are taking the ideas of here's some type definitions and Python.
And what could we, what could we do with that? Can we actually make that mean something to help
the developer experience? I think that's true. And I, you know, I guess I got some stuff right in doing,
doing documentation quite well, quite early on. I know that like it wasn't perfect, but you know,
it did the job at the time FastAPI and Sebastian's, you know, Sebastian's amazing in lots of things,
but his, his capacity to write documentation that is almost a story that almost leads you,
you know, it's enjoyable to read in the way that documentation normally isn't, obviously
yeah. Being adopted by FastAPI, like strapped rockets to Pydantic. But I think the other thing that
made an enormous difference is that I came to Pydantic as a developer, not a, not a typing
academic. And I know there's a lot of debate about whether or not the typing world of Python get moves
too far into the world of, of like the theoretical, but I always wanted it for me, it was always obvious
that a string of one, two, three should be coerced to an int. And there's a lot of people who will say
that's not useful. And then there's a million different ways in which they use it. And they don't even
realize because you think it's really obvious when you have ID equals one, two, three in a URL,
that that one, two, three is, is an integer. But obviously when you're passing a URL, there's,
there's no, no way to say that is actually definitely an int. So some of the, some of the lax stuff,
the coercion, I think has been the thing that sets Pydantic apart from some of the other libraries that
were perhaps more formally correct, but I would argue less useful in lots of contexts.
Well, I also think the more that you work on the web, where what you're accepting is out of your
control, you want more help and you want more validation and you want more guardrails.
People are posting JSON documents of who knows what to you there. There's the query strings and the URL
parameters that are always strings, no matter what they're supposed to be and stuff. So yeah, I think
Pydantic especially fit well in the API side of things.
I also think there's a, there's the risk of getting a bit kind of like fuzzy and cod philosophy about this.
There's a like, there's a value in remembering what it was like to not be that good a developer and
making it easy to use for beginners. And there's definitely a world of developers who,
who want to, whose primary interest it feels is proving how much they know rather than making it
easy for people. And Sebastian is even better at this than I am, but I think Pydantic does a good job
of it, of being easy to use. And if you're new to developing, you know, you and I know that I've seen
bytes and string, and obviously we would, you know, laugh through our nose at anyone who got them
confused. But the fact is that when you're new, they look like two identical things and one's got a B at the
beginning and the other one's got an F at the beginning. And what's the, what does any of that
mean? Right? Like, yeah. And so, ignore that part. Yeah.
Right. And so the fact that, that you can pass bytes to us, to a string field saves people a lot of
head scratching.
Yeah. It certainly has taken on a life, quite, quite a life in the Python space and many,
many different frameworks and libraries are depending upon it, which is great. Some stats
that you put in this article, we're going to talk, or this plan that we're going to talk about,
it's for 72,000 public repos that I'm guessing are expressing some kind of dependency on.
Yeah.
And then 10,000 GitHub stars. Yeah. That's coming up on 11. That's, that's pretty amazing.
And the, yeah. And the download count, I think it was 24,000 when I, a month to, sorry,
24 million a month when I last looked from, from PyPy. And that doesn't include distributions.
Pydantic is distributed with, I think every major Linux distribution. So downloads in those contexts
won't, won't be included in that. So yeah, it's, it's like, it's being, it's widely adopted and it
seems to be getting more widely adopted as, as time goes on.
Just to back up what you're saying to an army captain out there says, Pydantic is very easy
to onboard. Yeah. It's just because it, it does what you would expect it to do, what you would want
it to do. So let's see. One thing I wanted to sort of touch on a little bit before we got into the plan
officially is let's just highlight some of the frameworks that are making core use of Pydantic.
Obviously we talked about FastAPI, right? For people who don't know, maybe tell them real quick,
what is FastAPI? FastAPI is a amazing web framework that allows you to, I think if you
scroll down, I think that probably pictures will be better than words. you use Pydantic and,
and types generally to define what, what data people can pass to your, to your endpoints primarily as
per the name for designing, for developing APIs. and yeah, it makes it super simple. I think there's
an example down, down somewhere a bit further down on the, on the homepage. maybe there isn't,
maybe it's on getting started, but, there we are. yeah, you see here, whether it be URL
parameters like, item ID, or query parameters, or obviously the body, they're all
validated with, with Pydantic, which like cuts out enormous amount of the work of building, building
APIs. Absolutely. And then there's a couple of things that are interesting. You have Pydantic
models, which are Python classes with type, you know, a field colon type. So you express the type
information about it. And then you can say this API function just takes one of these and it'll
automatically pull that data in and validate it using Pydantic through like the body. But then also
you can express that that is the response model or the input model, and it'll use open API to actually
generate the documentation. So there's all these different ways in which FastAPIs made better.
Yeah. So the powerful thing about FastAPI is that by defining a relatively small amount here,
we just defined it as like three line function to define our, our endpoint, we get JSON schema for,
for the input we get, so then we get docs built off, off that. when we get obviously docs on the
return type, if we, if we annotated it with, with what's returned. so yeah, from,
um, and obviously the value and I can see from your, from your tabs where you're going to go next,
it's like you define something in one place and you can then use it for your input and for your
return type and then in your database. Yeah. And so yeah, here's the, this is the most well-known
example for using it on the API layer, the web layer, but there's also some cool examples of databases
as you pointed out there, right? Yeah. So did this surprise you when you saw these? I mean,
you probably had the API stuff in mind, but did the database surprise you? It did a bit. I mean,
I, I like, I haven't looked at Beanie in lots of detail, but like, yeah, it's, it's, it's like,
yeah, it's amazing that these things are coming along and being built and, leveraging, like,
yeah, leveraging what Pydantic can do. I'm not a big ORM fan myself. I'm a bit old fashioned.
I like to write my SQL. not, sorry. I like to write SQL, not my SQL.
so I haven't actually used, used them. I have to say, but FastAPI I've used a lot and I've,
I've found absolutely amazing, but I, I, I won't, can't talk about, Beanie or SQL model beyond,
uh, beyond having had a quick, quick look. Yeah. So I just want to give a quick sort of,
uh, awareness shout out to Beanie, which is an async ODM object document mapper from MongoDB,
like an ORM, but there's no R. So B for document based on, on motor. So it's pretty cool. It takes
the asynchronous driver from MongoDB and then Pydantic. You just express your models,
your documents as Pydantic models, which map really well because you can have hierarchies of
Pydantic classes and models, which maps perfectly to document databases. So yeah, this is actually
what Talk Python, the Python Bytes websites are built on, which has been really nice. And then
obviously Sebastian Ramirez created SQL model, which is the same idea, but for SQL, right? It's built on
top of SQLAlchemy, but you actually define your classes as Pydantic models. And then that finds a
way to sort of work with SQLAlchemy to still do the same stuff that it traditionally has done. So.
Yeah. I think there was one of the complaint, one of the complaints people had was that I,
they were having to define their data twice. They would have a Pydantic model and then they would
have a SQLAlchemy model. And so, yeah, it's, it's not very surprising in a way that we found a way to,
to combine them into one. again, I, I'm not, I'm not an expert on the internals of SQL model,
but it, yeah, the two things look similar enough that like at a, at a first pass, you would think
it would make kind of sense to squish them together.
And one interesting thought about this is if you're going to work in SQL model, or you're
going to work in Beanie or something like that, and you decide, no, I actually want to switch to a
relational database, or I want to switch from a relational database over to MongoDB or something
like that. If it's all expressed as Pydantic models, like how close are you? You know what I mean? Like
it's, it's, it's very little work to sort of make that transition. So it's, it's cool that Pydantic
is this kind of like, and there's a, that's a cool project. There's a cool project that I was
discussing with, Adrian, I think it's Garcia yesterday, which is using Pydantic models to define,
uh, data coming in from already, Google, Google PubSub and from AWS SQS and potentially from
Redis. So again, it's the same idea that like, once you define your models in Python, it wouldn't be that
hard to switch from AWS to, to Google or even to like a database type, tool like Redis.
Teddy out in the audience says, we use data model code generator to generate our Pydantic models from
JSON schemas. Are you familiar with that?
Yeah. Yeah. So, so obviously just as you can generate a JSON schema from, from a Pydantic model,
there's a third party tool that lets you, you go the other way and generate Pydantic models. I
obviously won't do everything for you, validators and stuff, but it gives you the first, first start.
Let me throw one more out there before we dive into the plan, which is where we're going. How about
JSON to Pydantic converter? Have you seen this website?
I had, I, I did not know that existed, until now, but, but I guess it's using that same tool under
the hood, is it? Or we'll watch it. Maybe it's not. It may, it may be, I'm not actually sure. I
haven't seen it mentioned it, but it doesn't really say so. I'd say not because it doesn't, that's not
JSON schema, right? That's just, no, what you do is you give it an example. that's very cool.
You give it an example, JSON document. I'm a, till 27. So you give it a JSON document and it will
actually, when I first heard about this, well, Pydantic will already generate JSON. Like, no,
no, no, no. The other way you give it a JSON result and it will generate the, the data model
by looking at, and it actually, even if you have like hierarchical stuff, it'll create multiple
base model derived classes and all sorts. This thing is, this is, this is pretty sweet right here.
This thing.
That's pretty powerful. Kudos to whoever built it. Yeah. I hadn't heard of it, but.
Yeah. And I've thrown massively complicated JSON documents at it. And it says like, well,
it's going to take eight classes, but here you go. And it just writes them all. It's, it's fantastic.
This portion of Talk Python to Me is brought to you by the compiler podcast from Red Hat.
Just like you, I'm a big fan of podcasts and I'm happy to share a new one from a highly respected
and open source company compiler, an original podcast from Red Hat with more and more of us
working from home. It's important to keep our human connection with technology with compiler.
You'll do just that. The compiler podcast unravels industry topics, trends, and things you've
always wanted to know about tech through interviews with people who know it best. These conversations
include answering big questions like what is technical debt? What are hiring managers actually
looking for? And do you have to know how to code to get started in open source? I was a guest on Red
Hat's previous podcast, command line heroes in compiler follows along in that excellent and polished
style. We came to expect from that show. I just listened to episode 12 of compiler. How should we handle
failure? I really valued their conversation about making space for developers to fail so that they
can learn and grow without fear of making mistakes or taking down the production website. It's a
conversation we can all relate to. I'm sure. Listen to an episode of compiler by visiting talkpython.fm
slash compiler. The link is in your podcast player show notes. You can listen to compiler on Apple
podcasts, overcast, Spotify, pocket gas, or anywhere you listen to your podcasts. And yes, of course,
you could subscribe by just searching for it in your podcast player, but do so by following
talkpython.fm/compiler so that they know that you came from talkpython to me. My thanks to the
compiler podcast for keeping this podcast going strong. Let's talk about the plan. First of all,
before we get into the plan, I just want to say well done on this. You know, we covered this on the
Python bytes podcast three or four weeks ago, something like that. And the response was,
oh my gosh, this is incredibly detailed, incredibly well thought out. I think somebody audience commented
like there are companies that have been created and founded with less thought about the future and
doing that. So yeah, nicely done. Thank you. Yeah. I spent a lot of, a lot of time,
quite a lot of PyCon talking about this, to people and say, you know, talking about little bits of it.
There was a lot of it in my brain and Sebastian, who is kind enough to sponsor me, but also obviously
is maintaining FastAPI. It's kind of asking me what it was going to do. And I kept being like,
oh, it'll do that thing. And it'll do this thing. And then I, I got to the point of realizing,
and probably about 70% of issues on PyLantix issue tracker, I reply with, don't worry,
it'll work in V2. And I realized I got to the point where I really owed the community an answer to,
to some of these questions. in fact, the first bit of feedback I got from it was I'm dyslexic,
and I'm quite slow at reading and I, those, red time, notes never make any sense to me.
So I just put 10 minutes in at the very beginning and then, forgot about it as I extended it and
extended it. And the first feedback was great article, but how the hell is anyone reading that
in 10 minutes? And so I pulled a new number out of thin air, but.
Yeah. So yeah, it's 25 minutes reading time, which I think is actually fairly accurate,
depending on how thoughtful you think about these, these various things.
Someone had, I've got to, I've got to have a shout out to one joke on Twitter. Someone was like,
when it said 10 minutes, they were like 10 minutes to parse two days to validate, which I thought.
Oh yes. Well done. Very, very, Pydantic. Like, okay. Why do we need this plan? What's,
why do we need to start?
So I think, I mean, like stepping back a bit, most projects, once they're mature and in, in widespread use,
like people don't sit down and tear them to pieces, right? they mostly stick with the
same kind of warts and people polish the edges, but like that, that, that there's not a like
from scratch rebuild. And often when there is from scratch rebuild, it, it offends a lot of people
because they don't know what's happening. And you know, they're like the, the, you know, the cost of
migrating is quite high and they're, they're turned off it. So, but I thought that there was,
there was enough wrong with the internals of Pydantic and there was enough opportunity to do stuff
way better. And there was enough, like, there was enough reason to do that because there were
enough people using it that it was worth me sitting down and spending six months, but we've passed six
months building it. Right. and like I say, this is, you know, there was one of the, one of the,
like not stats, but one of the, my observations was, looking at, so there was a, it was a stack
overflow, survey of what, of what technology people are using and FastAPI had, I don't know what
percentage, but like 6%, market share. Right. And then below it, they were
talking about clouds and which clouds had what market share. Now, if you assume the same number
of people are using web frameworks as they're using clouds, which is an approximate approximation,
but not a mad approximation, then you would say the FastAPI and therefore Pydantic have a bigger
market share than Oracle and IBM combined in slightly different markets. And obviously without the,
without the revenue to go with it, but like, it makes you realize that like getting this right
has a massive effect on, on lots of people and on, yeah. And, and secondly, that I don't have a clue
how many, how many times Pydantic validates data a day between, you know, Netflix and, Facebook
and Amazon and Microsoft and everyone else, but it's a high number, right? And so the environmental
impact of making Pydantic 10 times faster and therefore consume 10 times less CO2 to, to do a
validation is I suspect not trivial. It's virtually impossible to, to get an accurate number, but
something real.
That's a really interesting way to think of it with, you know, almost having a responsibility
to lessen the compute load. And when, you know, you're running your own website and it does a
couple of users an hour or whatever, like who cares, right? But when you're talking a million requests
a second or whatever it is across all the different people using all the different frameworks across,
right? That actually, that's what I suspect. And like, think about, think about, a web server,
assuming your database is doing all of the heavy lifting, that it should be doing. What's the
next biggest thing? Well, there's, TLS termination that's expensive, but like, again,
that's done by some optimized C and Nginx or probably outside your code completely. If you're using a
platform provider, what's the next biggest thing that your code is doing CPU wise? Well, it's,
it's data validation basically. Yeah. Conversion, serialization,
deserialization, validation, and all that was in the Pydantic realm. Yeah. Also I talked about two
frameworks and I know there are others, like Pydastic for Elasticsearch where the, the validation
and the data exchange is the database exchange as well. Right. That's, that could be very important
for if you make this much faster, I don't know the numbers for Beanie precisely, but I know that a lot
of those ORM ODMs, if you go and query and get like 10,000 rows back, the vast majority of that
time is how do I construct and fill out 10,000 objects in memory? Right. And if you make Pydantic
faster and Pydantic is that object, well, there's a huge bonus. It's more than just, and I think the
other thing to say is we've talked about, web applications and, you know, from FastAPI being the
kind of most high profile user of, of Pydantic. We talk about that a lot, but a lot of its usage,
if you look at, stuff that Explosion AI are doing, it's in data science and AI and it's, yeah,
it's exactly that. It's like data sanitization into and out of, models or into and out of databases.
And there you are talking about like, you know, really massive amounts of data.
Absolutely. All right, let's get into it. So we talked about the plan. How about the,
the roadmap, the timeline, things like that?
So we're behind a bit, but we're not too far behind. I released, version 0.1 of Pydantic core
yesterday. So that, I'll come to what that means in a minute, but, but that's, that was the,
that's the first step of the plan. I'm about, I think I've either closed or merged 25 PRs today,
trying to get through Pydantic and get, get version, V 1.10 out. So I'm, I'm halfway,
I'm not halfway through. I'm some bit of the way through two to be, to be precise.
right. And so what, what are you were talking about in the plan? As you said, there's a bunch
of open PRs, a bunch of open issues. Let's merge in as much of that as possible to sort of capture it
and then move forward in this, this rewrite that we'll talk about.
Right. Yeah, exactly. So get, get 1.10 out, which is, which is the same, same basic code base with a
bunch more stuff added that I've, because I had a, had a job and was really busy earlier in the year,
I kind of dropped the ball on reviewing those PRs and they, they kind of got out of control.
like get them, dealt with and then get to a kind of clean slate and then, and then, make
the big move from, yeah, from V 1.10 to, to V 2. You do talk about there being breaking changes.
We'll get into some specific details there, but probably the most relevant to this entire rewrite
is this thing you're calling the Pydantic dash core. Yeah. So, this started off as a,
as a kind of small experiment with me saying what would kind of thought experiment, what would,
what would Pydantic's, what would Pydantic look like if it was implemented in Rust? What would its,
uh, internals look like if they were implemented in Rust? And that experiment effectively worked.
And sure enough, Pydantic core is, is written in Rust and does all of the, the core data validation.
Uh, and it will do a lot of the serialization. I haven't built that yet, but that I intend to build
into Rust. So there's an awful lot that will stay in Python. but, yeah, Pydantic core is
written, written in Rust and uses the amazing Py03, library to bindings to, to write Rust code that,
that, that's callable from Python. yeah.
Maybe tell people about Py03 real quick, because this is how you write it and write the code in Rust,
but then expose it to the rest of the Python aspects of Pydantic, right?
Yeah. Py03, I'm not a good C developer and I'm going to use the wrong terminology and be
shy to that, but like it takes the Python ABI for C. So how you would write C codes to be used from
Python and effectively makes that available in Rust. Rust has, has great interop with C.
and so, yeah, it basically takes all those types and exposes them all in a type safe or type safe way
that you can then consume. So if we look, we stop here and we look at like, Summers,
uh, a string, right? Where, Py03 is taking care of all the hard work of you passing two,
ints from Python into this function, converting them to u-size. Then the logic inside is pure Rust.
It's adding to u-sizes and, converting the result to a string. And then again,
Py03 is taking care of returning it. And in particular, using this PyResult,
result type in Rust without going too far down the rabbit hole of how Rust works. Rust has an amazing
model for how to deal with errors that basically stops you from ever ignoring an exception or what they
call an error. and that is, that's these results, which are basically, they would call it
an enum, but from Python world, think of it like a union, which is either okay. It went well or error.
It was an error. And so you have to return an okay, or you have to return an error. And when you consume
that in Rust, you have to have to deal with the error case. it won't let you ignore it.
but that, that maps really nicely into Python exceptions. So here we're returning. Okay. So we'll get a result.
But if we use py error and return that, then you would get an exception when you, when you call the function.
Mm-hmm. Interesting.
The, so the powerful thing about Rust, obviously it's faster. Everyone knows that. And it does,
it does mean that Pydantic core is much faster than Pydantic and Pydantic 2 will be much faster than
Pydantic 1. I think it's probably quite rare to see a library in a version update, get, get significantly
faster, let alone like 10 to 50 times faster as Pydantic 2 will be. So that's been achieved, but there are
other advantages that you get, which are perhaps less obvious. one of them is like recursion
without a performance penalty. That means that Pydantic, core data validation is, is truly
recursive all the way down and allows you to build effectively any crazy combination of different
validators, into each other. Cause yeah, validators are this basically pile of, think of them as
think of them as classes in Python. They're not that, not classes in Rust, but that call each other
recursively all the way down. and you can, one of the other advantages is like tiny functions,
which allow you to split code up and make it easier to edit one thing without breaking other things.
Uh, because in Python, it's not entirely obvious to people coming from languages like C, C#, Rust,
and so on that just calling a function itself is pretty expensive relatively speaking in Python.
Yeah. I I I'm on the edge. I'm on the like wing of people who would say,
if you're worrying about the overhead of calling a function, you're probably not writing the right
language most of the time, right? Like it's, yes, it's, yes, it's a big number, but it's a tiny number
in, in most, in most contexts. But I think there's definitely a world in which like end users,
people building web apps in Python definitely for companies should and will be using Python.
But the libraries that underpin that, that they use super, there's big value value is a complex
term in open source in itself, but let's use the word value and ignore that what it might mean
in, in implementing those libraries, the second step down. So the Pydantic, the, the HTTP,
framework, in, in Rust or in, in, I think in Rust basically, because those are the,
well, there are three libraries that have real bindings for Python. C, I don't want to be writing
lots of C and I don't think many people do. well, I'll say that, Rust obviously. And then
there's C++ and Boost. And I think the, the developers of, Py03 came from using Boost and
they basically built Py03 to be better. and I, yeah, I've used Boost a bit, but I found Py03 to be,
to be really impressive.
I think your comment about, should you be worrying about those loops is super relevant. There's
certain libraries where Pydantic is certainly among them. It's used so much that these little tiny
portions, you know, probably just a very small slice of the code that is applicable is actually
a pretty significant hit in terms of overall performance. You know, you think like SQLAlchemy
and like the serialization deserialization bit, right? That's a small part of the library,
but that's something that just is ever, you know, omnipresent, right? And this internal
validation and stuff that you're thinking about doing in Py03 or in Rust, combine it with Py03,
it makes a big difference, even if it's only a small, relatively small portion of the part that
people perceive it to be, you know?
Exactly. And coming back to my environmental point, you know, the environment doesn't care if you take
a flight or I take a flight or I miss a flight, but like, obviously the environment does care if we
can reduce the number of flights taken worldwide by 10, worldwide by 10%. And because of Pydantic's
widespread use, that's why I'm saying getting Pydantic to be 10% faster, you probably won't notice.
But overall, we will hopefully make computation in the cloud a tiny bit faster.
Absolutely. Question from the audience. Magnus says, will users be able to write data validators in
Rust for Pydantic too? That is a difficult and complex question. There is an open issue on Pydantic
Core's issue tracker about it. And I have proposed a way that it might be possible. I would, the story of
shared libraries, DLLs in Rust is not quite as pretty as it could be. And I really don't want
to build basically another way of sharing dependencies beyond PyPy where you're like, okay, you need to
install Pydantic from PyPy, then you need to install this other package, perhaps from PyPy, and then you
need to use this other code to link the DLL so that we can dynamically link those libraries. That sounds
like an enormous maintenance overhead for me and for people doing it because people find it hard enough to
share code and use code from PyPy. So PyPI. So yeah, then how do you deploy that and how do you get it
compiled? Right. So very briefly, my theory for an answer is actually, I'm not going to go down that
rabbit hole right now. But there's an issue that I think explains it and I'm happy to talk about it there.
And I can, I'll finally link to it. If you don't add it now, you don't have to live with the consequences of
choosing that. That's the other thing, right? That like someone comes along and has a really bright idea and in
10 years time, I'm still answering questions about how to make it work. Yeah, exactly. Okay.
You already mentioned the performance, but just working our way through the plan here, the next
step is to say, hey, the benchmarks indicate this is four to 50 times faster. And in general,
17x is kind of what you're guessing for something reasonable.
Not guessing, as in just the benchmarks on Pydantic Core that are run on every commit,
a lot of them have alternate equivalents in Pydantic 1.9. And so that's the speed up that we're seeing.
There are a few more optimizations I can make. There are a few, it'll get a tiny bit slower,
I guess, when it's wrapped in Python, but a tiny amount. So yeah, I think those are realistic numbers.
Yeah, that's a huge difference. Now you say, when validating a model,
how does that performance compare to treating a Pydantic class instance? How much faster does
using it in Python get versus... 17 times faster, doing the validation. You should get your model
back. So that is going from a Python object, a Python dict, let's say, of your input data to a
instantiated class instance of your model.
David Miller: Next up is strict mode. One of the things I really like about Pydantic is how
it will take data that could be the right thing, but it's not actually the right thing. Like you said,
the string one, two, three, but you really want an integer, the actual number one, two, three. And it just
says, "This is what we would do if I had to do it myself. I would parse the string and convert it
over and so on." That just happens. But some people don't want this clever behavior, right?
Yeah, exactly. And I think that there are legitimate cases for that. I think there are
some people who are wanting it whose cases I don't think are entirely legitimate, but I totally get
why in some contexts it's valuable. And so, yeah. So it's built in. You have that switch from the word go.
David Miller: One of the really cool things that this solves,
David Miller: kind of not by mistake, but as a side effect is validating unions. We basically run
through every member of the union in strict mode first and try and validate in strict mode and then
validate in lax mode. And therefore, for example, if you had a union of int and string, and then you passed
it the string one, two, three, it wouldn't get converted to int as it would do in historically
in Pydantic. Pydantic now has smart union, but it's not perfect, but this solves some edge cases like
that and some much more confusing ones than that. Nice. Related to that, I would say, is this conversion
table that you're putting out, right? What's the story here?
David Miller: Yeah. So there's two things. There's this, I kind of called it cod philosophy the other
day, this rule for when you would convert something and when you wouldn't. And actually,
it's come out to be really useful in us thinking about when we shouldn't convert things,
because to take an example, we have been in Pydantic v1, you can coerce a set to a list.
And that mostly seems to make sense. And it's something that you might want to do in lots of
contexts, but actually you go up a bit, the single and intuitive means we can't converse,
convert a set to a list because you don't always get the same output when you convert a set to a list
because the order of things can change. And so using this rule has been helpful in trying to be
more consistent about what we convert. But I'm the first to put my hand up and say,
this rule is not perfect. There are always going to have to be exceptions to it. And at the bottom of this
blog post, but then properly on the docs completed, will be a full on table of everything and what gets
converted and what doesn't in max mode. So you can look it up rather than having to guess.
This portion of Talk Python to me is brought to you by Microsoft for Startups Founders Hub.
Starting a business is hard. By some estimates, over 90% of startups will go out of business in just
their first year. With that in mind, Microsoft for Startups set out to understand what startups need
to be successful and to create a digital platform to help them overcome those challenges. Microsoft
for Startups Founders Hub was born. Founders Hub provides all founders at any stage with free
resources to solve their startup challenges. The platform provides technology benefits,
access to expert guidance and skilled resources, mentorship and networking connections, and much more.
Unlike others in the industry, Microsoft for Startups Founders Hub doesn't require startups to be
investor backed or third party validated to participate. Founders Hub is truly open to all.
So what do you get if you join them? You speed up your development with free access to GitHub and
Microsoft Cloud computing resources and the ability to unlock more credits over time. To help your startup
innovate, Founders Hub is partnering with innovative companies like OpenAI, a global leader in AI research
and development to provide exclusive benefits and discounts. Through Microsoft for Startups Founders Hub,
becoming a founder is no longer about who you know. You'll have access to their mentorship network,
giving you a pool of hundreds of mentors across a range of disciplines and areas like idea validation,
fundraising, management and coaching, sales and marketing, as well as specific technical stress
points. You'll be able to book a one-on-one meeting with the mentors, many of whom are former founders
themselves. Make your idea a reality today with the critical support you'll get from Founders Hub. To
join the program, just visit talkpython.fm/foundershub, all one word, no links in your show notes.
Thank you to Microsoft for supporting the show.
Before we move off strict mode, well, Clutch just has some kind things to say about Pythonics as it's
one of the most useful packages ever. Congrats.
That's really cool. Magnus asks, is strict mode a global or a per model setting? Or is it a usage
when you actually do the parsing? Where do you set this?
It's actually more powerful than that. It is either on a field or on an entire model. And you can set
it at validation time. So you can configure it in config and configure it on a particular field,
and then you can override it when you're effectively calling the validator.
I see. Maybe there's some situation where you're loading old bad data or something,
and you want to say, go ahead and do this, but in the future we're not accepting it. Something like that.
Right. And actually, one of the reasons I built that was to use it in the union because we go through
the validators the first time at validation time insisting on strict mode. But yeah, one of the other
cases which will come up somewhere down here is we now have a isInstance or a like pseudo isInstance
method which confirms whether data matches our model. And there we automatically use strict
mode because for me it's kind of obvious that if you're doing isInstance, you want that to be checked,
want that to be strict. Moving on to the next part of the plan is built-in JSON support.
Yeah, so this is super. What are we talking about here? Yeah.
So we're talking about parsing JSON in Rust and parsing that JSON object straight internally within the
library to the validator to then do the validation. One of the big advantages that has is it solves the
strict mode problem. So if you looked above, let's say we have the string of a date, let's say, you know,
an iso 8601 date of year month day. In JSON, it's obvious that that should be validated as a date. But if
you pass that in from a Python object, it's not valid in strict mode, right? That's not, that doesn't look
anything like a date. The problem if we had strict mode before without the the built-in JSON validation
is you can't parse JSON with a date in it because there's no date representation. There's no scenario
where directly going from JSON works because JSON, for odd reasons, has no concept. It doesn't have date,
but it also doesn't have sets or bytes or loads of stuff that you want to use in Python, right? So
what one of the things that built-in JSON support gives us as well as obviously a performance premium is
is that we can be sensible and say the iso 8601 date is a valid date in strict mode if it's coming
from JSON, but not from Python. Okay. Yeah. And also just makes it faster, right? Because
probably parsing JSON and Rust is pretty quick. It's really, it's fast. But also we don't have to
create a Python dict on a Python list and all those Python types. Creating Python strings has like some
significant overhead compared to creating a string in Rust. And in future, once I've got V2 out, I intend
to build a custom JSON parser, which is even faster and will give us line numbers in errors, which would
be really nice because we don't have that now and we can't do that in V2 because so JSON, which I'm using,
doesn't provide line numbers. But I hope in V2.1 or something, we will be able to add that.
Amazing. Really quick on the strict stuff as well. Manaj asks, what about strict int as a type? Is it
going to be still around? That can stay around because that will just be, that'll be effectively.
So it's probably worth this stage for people. If we, if you could just go to Pydantic cores repo and we'll
have a really brief look at what it looks like. Yeah. And then just in the read me, you'll see
a example. So you see here, right? We, up a bit, up a bit. You don't need to go into all the details of
it, but the way that we define the model in Pydantic core is with this kind of like micro schema,
which is defining in this case, a type dict with a bunch of fields in it. And here on a particular
field, we could say strict true. So let's say on the int field, we could say strict true.
And that field will be strict while the rest isn't. So obviously what strict int, the Python,
the Pydantic type will do when it becomes a schema, it will set a strict true on that particular field.
Got it. So it effectively is a synonym for the more general way to say,
use strict mode, but only on this field, right? Yeah, exactly. It's just a market effectively set
strict on this field. Yeah, exactly. So in Pydantic, you can say I have, say,
an age, which is an int, and you can set it to a default value like zero, or you could say it's
optional, set it to none, but you can also set it to a field, right? Where you have additional
information. Is that how you set strict mode? You set it to a field and say strict mode equals true
or something like that. It's not built yet. So it's up for debate, but yeah, effectively strict will be
a setting on field and obviously on config as well. And there will be these types, which basically
contain some extra information like strict int will just like set that strict to true for that field.
Right, exactly. And for people who are not aware, config is an inner class of the Pydantic model that
has a bunch of settings you can set, right? Yeah. And people do some unholy stuff of like modifying the
base version of config and therefore doing global stuff, which I've never done. People seem to make it
work. I don't know it'll work in V2. I don't promise it will. Yeah, absolutely. One of the things
that's interesting with this Pydantic core is now this is a dependency of Pydantic, right? And people
could use it directly if they wanted, right? Like validating without a model, you don't have to define
a class or any of those things. 100%. You don't have to define the class. If we look in the example we were
using there, we didn't have a class. We were just validating to a type dict. So we would get back a dict,
which obviously means we have full support for typings type dict type. It's also a little bit
faster than creating a model because we don't have to create the class instance. We just create the
dict that goes inside it. Yeah, people could use it without. The only concern obviously is whether or not
obviously it's now compiled and you have to be able to run that Rust code to be able to use Pydantic.
We, with the V0.1 release of Pydantic core yesterday, we have, I think off the top of my
head, 56 different binaries that we released for different environments. The team of the guys at
Py03 and at Maturin, which is their way of building, have been super helpful and will continue to support.
So it doesn't worry me. We already have the full Pydantic core set of unit tests running in the browser
via WebAssembly. So obviously Python moving into the browser with WebAssembly is like the big new thing.
I'm really excited about it. I wanted Pydantic core to work. And so Hood, who's one of the
Pyodide maintainers, I met at PyCon. He's been super helpful actually with Pydantic core in general,
but particularly with getting it to work. And at the risk of running a live demo, if you just go back
to Pyodantic core, I know we're slightly changing subject, but I have to show you this because it
makes me really excited. We go up and you go into WASM preview, which is one of the directories.
Yeah, WASM preview. Okay. And then if you click here, which just basically renders that index file,
I hope it works. This is... Gotta work. Gotta work.
It's gotta work. This is it downloading the binary, downloading all the unit tests,
extracting them in Python and running the full test suite in the browser.
Let me try it one more time. Do it a second time. Yeah. So what we're seeing, if you click on this link,
which I'll put in the show notes, is it downloads the CPython runtime in WebAssembly based on Pyodide,
I'm guessing. And then... Then it downloads the archive zip, sends that to Python. Obviously,
we're running full CPython in the browser, so we can use the zip package to extract zip,
extract that into the virtual file system that Inscription gives us. Then we install the WASM32 wheel.
We basically do pip install, well, micro pip, which is the way of installing stuff. And then we just call
pytest and off it goes and it runs the test. And you see the test come by, standard colorized
pytest output, 1465 tests pass in five seconds. Pretty fantastic.
Yeah. So it is a bit slower this than full CPython, but I'm still really stoked for what this is going
to mean to the future of Python and particularly to stuff like the context where you might use
Pydantic of data processing and stuff. I don't think Python is going to replace React. And I think it's
a bit daft of people to suggest it will because that's just going to lead to disappointment. But in
contexts like this, it's going to be super valuable. One of the things I'm really looking forward to is
Pydantic 2's documentation. Every single example is going to be executable. So you can edit it and you
can press run right inside the browser, which I think should help a lot.
Have you been tracking PyScript?
Yeah, I have been tracking PyScript. It's obviously, it's very cool. It's wrapping PyDide,
which is where all the genius work is going on. I'm using PyDide directly and I think I can continue
to do that. But yeah, it's providing a bit of a super helpful wrapper for those who need a bit more
help and it's simple as a script tag.
A question from David out in the audience asks, "With at least two of your projects switching to Rust,
Pydantic and watch files, do you see it as a general trend in the Python ecosystem,
you know, in things like PyScript, which I just pulled out?"
I have a third one actually, rtoml, which is a wrapper around the Rust-toml library,
which is a bit less necessary now when there is better tomml support in Python. But a couple of years ago,
when the main tomml package was not working for me, I wrapped that. Yes, I do. I was saying earlier
that I think lots of the low-level tools should be written in Rust. There is a massive space for
someone to go out and build a raging fast ASGI framework in Rust and obviously use a Rust web
framework and just provide ASGI interface. I'm looking forward to someone doing that to replace
the likes of uvehicorn. Not that uvehicorn is great. It uses watch files, in fact, so not to
criticize them, but like, yeah, there are a bunch of low-level stuff where performance matters,
which totally and I think should and will end up being more in Rust.
You're suggesting something like what you have for Flask, but everything is Rust except for just
your view methods happen to be Python and click that together with PyO3 or something like that.
That's the ultimate place to go to. I think that the
place to start would be, so we have WSGI, which many of you will have heard of, which Flask and
Django run on. We have ASGI, which is the async equivalent, which is basically, it's great because
it means that to build a web framework, you don't have to deal with HTTP. You deal with ADDICT, which
has basically got fields and body and stuff like that, right? And some function to get the rest of the
body in the async case. And that's what we have now. And we have like Starlit and uvehicorn,
which are both built by ENCODE and are both great, but they have a separation by using this
consistent protocol in between. And that allows really cool innovation on both sides. My suggestion
is we don't have to get rid of the Starlit or the FastAPI or that level, but we could do lots of the
low-level HTTP parsing in Rust. Before I get shouted down, I'm sure that uvehicorn and other such
libraries are in turn using some Optimize-C for parsing some of the HTTP requests. So I don't have
a number for the speed up. Right. Okay. But yeah, that's a very interesting idea.
One thing I did want to sort of touch on here is you have, you talk about how there's not going to be
a pure Python implementation of the Pydantic core because it's already this complex, specialized thing
in Rust. And why do it again in Python just so there might be some edge case of where it'll run.
Talk about many of the platforms really quick that supported for the WebAssembly one we just spoke
about, which is fantastic. And I think that's going to open up a lot of possibilities the more
stuff we have in WebAssembly. But there shouldn't be a big problem with this, right?
There shouldn't. I think with what we have there, we've covered the 99%. We're probably into the 99.9%
of platforms covered where people actually want to use this. The only place where I know that there's a
slight challenge is on Raspberry Pi, where the normal install of Raspberryan or whatever it's called
uses their own wheelhouse effectively for installing wheels, which doesn't yet support
build of Rust. I'm sure it will one day and you can just tell it to use PyPI and it will work.
Again, this is the kind of thing where having built watch files and distributed that, I've worked through
a lot of these problems and I'm pretty confident we're not going to find some really important
framework, sorry, really important environment where it's just not going to work. And again,
as more packages adopt Rust, we'll smooth out those problems, we'll learn from them and we'll be able
to fix the edge cases.
One benefit of that is previously Pydantic itself had some Cython and other things where it needed to be
faster, but because now it can just use the Pydantic core, what's left over is pure Python, right?
Right. And one of the big problems, well, there were two problems with that. It made the development
process a bit slow because we basically took vanilla Python, we compiled it with Cython and we got a kind
of 50% speed up and we have to like do some slightly weird things. So occasionally you have to return
union of just string to prevent Cython from casting that string to a native string and losing sub strings,
stuff like that. Some weird edge cases that bite people occasionally. But the biggest problem is that
that means that the Pydantic binaries are massive because the Cython compiled versions of Python code get
really big and obviously moving the performance critical bit into Pydantic core gets rid of that
concern and Pydantic itself becomes a pure Python package, easier to hack on, CI will run faster,
whole process should be sped up and it'll be much smaller.
Let's go, I jumped around because I did want to talk about this compiled stuff,
but you'll just get that as a wheel. Almost everybody, they won't really know or care, right?
They just pip install it. It doesn't matter that it's Rust, it just downloads as a binary, right?
Right, exactly. Same as loads of packages you use now.
Now Pydantic included are compiled, right? They're all compiled, right? And if there is no wheel
available, then pip will do its very best to try and compile that for you. So in the case of Pydantic
core, if you were in some crazy environment where we didn't have a binary, you need Rust installed,
and then pip will take care of compiling it for you. But like I say, that's going to be super rare.
And realistically, if you have that problem, come and create an issue and we'll add the binary for you.
Picking up back on the plan here, you have required versus nullable changes.
We missed out one of the really cool things above. I don't know if I'm sorry if we're moving order,
which is the removing of the necessity for a model. So as we saw earlier in the Pydantic core
example, we can validate. So in Pydantic 1, everything was in the end of Pydantic model. So we looked
earlier at like FastAPI, passing parameters. In the background, FastAPI is creating a model,
doing a validation against that, then extracting stuff from the model and passing it to the function
or whatever else. Similarly, if you wanted to pass a type dict, you basically somewhere in the background,
there's a model, we validate against that model, then we take the dict from that model and pass it
back to the user. That had some really confusing and annoying edge cases. But obviously, the main thing
was it did have a performance impact. Now, there is no fundamental kind of base type in Pydantic core.
You can validate an int or a string or union of different stuff or a model or a data class or a type dict,
and you just create your schema and off you go. Fantastic. So basically, there's this low level,
fast engine that'll just validate all sorts of things if you want to use it directly, right?
Yeah. And the one thing important to add just while we're on that is there is stuff that's not going to be in Pydantic core.
I don't think we'll add the URL type, for example. There'll be some, you know, there'll be some custom types that we don't add.
And obviously, if you want to implement your own types, then the way that we get around that is that
Pydantic core has a basically a function validator, which is basically call a function, either having
done some validation before or after and return the result. So that's how we're going to we're going to
provide a way to build validators without writing without writing Rust.
Required versus nullable?
Yeah, probably like hangover from again, me building Pydantic on my own for what I needed.
And also from, you know, it's kind of predated data classes, at least in some of the work.
And so there was the real problem for me was the word optional and the idea that you had a field that
was required, but was literally called optional. Obviously, I'm not, Pydantic is not the only library that has that problem.
And the real solution is, is the pipe operator, which is the new way of doing unions. None involves not using
the word optional. You can obviously also get around it by using union of string int. But the point is that
if you just have a field that is optional int, it is required but can be none. And that's just that's really
just to match data classes and other contexts.
Yeah, the new way to express optional for like string pipe none versus optional string. Yeah, that that kind of
set you free to think about this differently.
Yeah, also, I mean, I literally asked we do about it at PyCon. And he was like, he didn't say yes,
we made a mistake. He said that's fixed by having having pipe none, which is a roundabout way of
saying we kind of made a mistake back then. But you know, typing has come a massively long way since
someone settled on the word optional. So I get it. But it has been a source of confusion that's now
being like cleared up. Yeah, for sure. And there's other things that have been
changed as well, right? Used to have to say from typing import capital L list to return a lowercase
list, but it'd be a capital L list. And now it's like, you know what lowercase list works too.
Right. You have to now we just have the weird side case of any where there isn't any function,
but you can't use it. But we won't. We won't go down that line.
Yeah, for sure. Want to talk about validated functions?
Yeah, I touched on them just now. And like I said, we have, we have the idea of before. So we do a
validation before and then we pass the result of that validation to a function. We have validate afterwards
and plane, which doesn't doesn't do any validation, just calls the function. The most exciting thing,
probably the one of the things I'm most stoked for in Pydantic V2 is these wrap validators. So you'll
have, you'll have read about middleware in Django or, or any web framework. We have this idea of an onion
where we call a validator, sorry, call a function, which takes a handler to call the next function.
We have the same, same thing here in, in, I'd antic V2 where we have these, I've called them
wrap validators. They take a handler to a function and then they call that the power here is obviously
we can, we can do some logic before the validator. We can do some logic after we can catch errors.
We can return a default value. It gives us like loads of flexibility to do, to do more powerful stuff.
Yeah. You basically can do whatever you want and decide to delegate down to the, the chain of
handlers if you want, or skip it, right? You say that this, this looks good to me. We're just gonna
return a value here.
In particular with, with Pynantic one, there was no way to skip, yeah, to skip validation if you
had a validator. So, which obviously caused a slowdown. If let's say you had date time now,
you still had to call the validator, which sure enough, got a date time and was happy,
but you had to go through that logic. Whereas here, we know it's a date time because we've,
we've written that code. there is a, there is the potential for people to make mistakes and not
call, the handler. if they wanted to return the raw value, then, we can't break,
we can't stop them, but that's Python. There aren't, there aren't always guard rails.
That's right. Yeah. Some of the power is in the flexibility. All right. But that, that lets you
do bad things as well. Yeah. I mean, we could, so sorry to interrupt. we could theoretically do
some crazy thing where we checked if the handler was called and raised an error or a warning. But
I think, I think at this point we let people make their own mistakes if they, if they insist.
Well, and you also would pay a performance price for all the places where it's used correctly.
Yep. More powerful aliases.
Aliases. Yeah, this is a features that I saw.
It's helping what the alias are. And then, yeah, then what's the use here?
Aliases are the idea that we, we have a name for what we want to want to call a variable in our code,
but we know that in the real world where the data is coming from, say on the front end,
it's got a different name. Often it's camel case, on the front end, because it's JavaScript and we want to
use, state case, in Python, but also, you know, we're using some API and we want to,
it has to be called something when the data is coming in. and so we had that in Pydantic,
uh, V1, the idea that you could have a field that was called something else externally.
but this is actually a feature I'd saw in, the rust third library, which is that the main
validation library, this idea of flatten. So basically take a value, not just from the top
level decks, but from deep down in some object, we pass it and use that for the field. And so again,
this is one of the things kind of stuff. Yeah, sorry. I see this stuff all the time where you'll
get some huge response from an API, but you're like, I just really want this little part here.
And so what you end up having to do is say, okay, capture that result. It's a dictionary,
then navigate down to the three levels and get the sub object and then pass that to Pydantic. And here,
you could just say the alias is sort of traverse that down and start from there. Right?
Exactly. And we get nice advances. Like if that thing's not there, we don't get an error because
the, the get, you know, none has no, you know, get method or whatever it might be. Pydantic will take
care of just saying that feels missing. If let's say, as was a, was a string. So therefore,
couldn't get as second element, you know, quarks, whatever that is. Yeah. So in the case you have here,
you, you say the alias is a list and the list is baz and then two and quarks. These are things that are
appearing in this JSON document. Yeah. The dictionary. So, so that list is, is effectively
some location, but it, what you'll notice again is there's actually another outer list because we can
have more than one of these. we can have it as deep as we like, I'm sorry, as many different
aliases to try as you want. Yeah. So this actually traverses down and the two means go to the third
item because it's zero based in the list and then look for that, that element. That's pretty powerful.
Yeah. And again, this is the kind of thing that we can do because Pydantic calls in Rust and the,
the overhead of, having aliases of, of multiple different types is basically absolutely
minimal because it's a, in Rust, it's an, it's a single enum lookup. And if we have a simple alias
of a string, we don't need to worry about any of that crazy logic to recurse down. We just take the
top, you know, an element out of the top level dictionary and move on.
Jonas asks, would this solve when my app gets Pascal case, I want to work with snake case and then return
a camel case. Is there some way to express that kind of stuff with aliases?
This does not, but we had, there was a pull request, for Pydantic two, Pydantic one,
that where we had load alias and dump alias. So a different alias when we were exporting.
and I, I do intend to support that. So this particular feature is kind of related, but won't
solve it on its own. But yeah, I do intend to allow two different aliases.
Speaking of loading and getting back out, improvements to dumping, serialization, export.
Yeah. There's been, there's a bunch of stuff here that people have wanted for a long time,
in particular, being able to create a, like a JSON compliant dictionary. but also people wanting
to do their own customization. Again, my hope is that because that, that dumping logic will be
implemented in rust, we can get like, I'm going to call it kind of zero cost
extra features because in the end it's like, should be just an enum lookup, to, to do the complex stuff.
And if we're not doing the complex stuff, we go the, we go the optimized path. yeah, there's,
there's what, what we've realized is there's a, there are a whole bunch of different things that
people might want. They might want the raw data, including, including sub models. They might want
what DICT does now, which is recursively convert, models into dictionaries, but otherwise keep stuff
unchanged. They might want to JSON compliant, DICT as I was saying, or they might want full
serialization to JSON. And obviously that last one in particular, we want to be quite well optimized.
Well, we want them all to be, but yeah, effectively.
Last one's the most important. Yeah.
Yeah. We, we want a, we want to be able to provide someone complete flexibility without it
harming a performance in the case where they're not using that. And that's, that's why I think,
Pydantic Core has allowed already on validation and I hope will allow on, on serialization.
All right. We're getting a little short on time. So let's maybe, let's, why don't you pick
out some of the remaining stuff that you want to focus on? I think maybe the most important is a model
namespace cleanup. What do you think?
I mean, I think context that I was just going to mention here, that's going to be another
amazingly powerful, escape hatch for some of the, some of the things people want to do.
Uh, obviously the main use of it is for, is for allowing validation against some dynamic data,
but you can also update that thing. It's just a Python object. So if you wanted some case that we
were talking about rapid validators earlier, where you've got errors and you want to raise a warning,
you could append to context the warnings. so that is another super powerful escape hatch
without, without harming performance for everyone else. How it's going to work with FastAPI,
where the model, where you do the validation before, calling user code. I don't know yet,
but I'm sure.
Yeah. How do you provide that data that is the context, right?
You have another dependency, I guess, in FastAPI, lingo that generates your context for that
particular call.
Yeah. I was thinking some of the dependency injection stuff, which is not very popular
in Python in general, but that might be the way you might register. Here's how to get the context for
these types of models or something. Yeah. I mean, Sebastian will decide, but that's what
we'll find a way to do. Yeah, for sure. One quick question just on usage here. Like I see that you're
saying user and then model validate JSON with this data. And you could also just say user star,
star data. I know you're doing this different here, so you can pass the context, but what,
what would you say is the best way to create these objects?
So, so, so, so model validate.json is going to be there and it's going to be named that or something
close to that. And the point is that's taking a string of Jason or in this, in this case,
a bytes of Jason and validating it directly. We talked about that earlier on. so that's not
the same as user star star, right? Right. Because it's bytes. there's also model validate
Python, which is effectively the same as model, star star data, except that because obviously we
basically don't trust anything you pass to it. That's all external data. You can't pass context
that way. Okay. Yeah. which I think comes onto your, your question about the cleanup of the,
of the namespace. There are some breaking changes and there's a decent number about sort of renaming
some of these, these model methods and stuff, right? Yeah. I'm not too worried about these because
we're going to leave the old functions there with a, with a depreciation warning on all of them.
So that will be quite easy. The stuff that's going to be really hard in terms of breaking changes is like
where, for example, I've talked earlier about sets no longer being coercible to a list.
There's no way to give a warning about that really without absolutely peppering Pydantic core with,
with like warning logic that, that would be horrific. so there are going to be the things that are
going to be most difficult for people are going to be like silent breaking changes. I'm not particularly
worried about functions that give you a warning when you call them and say, use the new name,
it's going to be the silent stuff or the, you know, the fundamental changes in behavior that are going to be
hard. But again, I, I, I, I, there's no way to make Pydantic better without doing that.
Yeah.
Fair. I think it's worth pointing out the error descriptions now have a documentation link.
That's kind of interesting.
Yeah. I think that's going to be super powerful for people. I don't know anyone who's ever used a cargo,
and Clippy, which are the rust tools for, for broadly speaking, linting and compiling. whenever you get an
error, there's a, there's a link basically to give you more information. And obviously a lot of these,
a lot of these links will be being shown to developers through APIs, and we can't provide all the information we might like in a,
like one sentence message. And so we're going to have these, have, a bit of Pydantic's,
docs dedicated to information on every single warning, every single error message and what can happen.
it leads to another interesting question about Pydantic two and what we do with the documentation
and the licensing of it. So Pydantic is definitely going to stay MIT licensed, might be dual licensed Apache
two, if someone can tell me why that's necessary, but it's going to stay, you know, permissively licensed,
but I'm, I'm kind of becoming aware that the documentation, which is valuable and will get
better and more valuable. It's currently MIT licensed and some company could, could take it
all and bang it on their, their, their domain totally legally. So I might change the documentation
license to something a bit more restrictive to say, for example, you can't take all of these, error
message, documentation and just put them on your own domain. Or at least we have, we have some way of
making that possible without make allowing people to commercialize that mostly because it would get
really confusing if there was Pydantic documentation is up to date and Fubar company who published the
whole same thing, but leave it out of date. And they both come up on Goop.
It's interesting to think about having this mixed model in your repo, because obviously you want
Pydantic, the library to be wide open for people, but then there's this supporting stuff.
And I mean, I treat differently.
Yeah. And I know that the, the unit, the Linux distributions are going to be super spiky. If any
of that stuff that's not MIT licensed got distributed, right? Because they're, they're, package managers
have to have stuff that's, that's, you know, correctly licensed. I mean, obviously they allow
stuff that's, like GPL or something, but I I'm, I'm thinking about something from is GPL doesn't
stop you publishing documentation. So yeah, it's an open question. I don't want to have a separate repo
for documentation because it will make creating a PR that much, you know, higher friction. But,
I think I need to talk to an IP lawyer before I say anything authoritative on this is what I guess I'm
getting to. Yeah. I'm, I'm feeling entirely unqualified to, to give any advice on this,
but it's tricky, right? As we were talking before you hit record, like if you have a sub light,
a license in a sub folder, does that license override the more broad one? I do you have to go
and change your broad license, your MIT license, say, here's the MIT license, except for this section
of the, the repo. This doesn't apply to see it's license. You know, that's weird.
I presume that the big projects, the Django's of this world and the num
NumPy must've, must've thought about this stuff. So probably worth doing some research on them,
but I'm thinking out loud and I probably need to come up with a conclusive answer before.
Sure. Well, it's called a plan, not a release, right? Okay. we talked about the
identity becoming its own license. One that I want to talk about is the from ORM and friends,
I guess. yeah, maybe talk about this, these sections here.
So there's a whole bunch of improvements here that we could talk about for, for an hour,
probably on each one, let alone, let alone full, but, but the, the from ORM was a, was a,
was a bit of a strange case where it was, you had to have a conflict flag and then there was a method
on a, on a model. pydantic-core has this built in from attributes power, which basically allows
it to recurse through some Python object that is not a dictionary instead of a dictionary, if you switch
that on. So we talked earlier about aliases and about hunting down through some complex objects,
normally of dictionaries, if that came in from JSON, but like in lots of contexts and ORM in particular,
it's, it's not right. So from attributes, it lets you basically do that same finding things in an
object from something that's not a dictionary via basically get atro, not get item effectively.
Yeah. And that makes a lot of sense. Cause then you could just pass any class that you got from
anywhere. You don't have to find a way to get it to a dictionary.
Yeah, exactly. And, Pydantic should, should take care of that and give you nice warnings when
it, when like the third level, it gets a, it gets the right error or it gets a, it gets a type error.
It'll tell you type error. And if it's an attribute error, it'll say not found.
So yeah.
Got it. Yeah. So from ORM to me, that felt like, well, here's a thing, a way to integrate it with
SQLAlchemy or something like that. But this is just more general to say, we're moving to something
that just says given any object, just go get it.
Yeah. I mean, from ORM was a dumb name. You're quite right. It came exactly from,
from, compatibility with ORMs and SQLAlchemy, but in particular, but yeah, in what we're actually
doing is taking stuff from attributes. So the new name makes more sense. And the new functionality is
like a lot more powerful.
Would from attributes work on properties in addition to fields?
It should do. Yeah. Yes, it does. There's a unit test for it. It does.
Okay. Oh, fantastic. That's really cool. Cause your, your class might have computed elements,
but you want them to show up in your JSON, right? Or something like that. So yeah.
Yeah. Cool. All right. I think, I think that might be, I want to know one more question I have for you.
When I was doing C, C++ C#, I remember thinking about numerical types a lot. Is it sufficient
to have an end here? Do I need a long? Is it an unsigned long? How much data could it be? What happens
if I have an end and I increment it now it's negative 2.1 billion or whatever? Like there's
all these weird scenarios that go away in Python because Python uses a slower, but way more flexible
numerical type, right? All this stuff happening in rust. I feel like you might need to think about
that a little bit. Yeah. So it's, it's all, I 32 in the case of, in the case of ints. So we're
limited to whatever, so I 64, sorry, I 64. So whatever the limit is on, on I 64, that does
mean that you can't pass in. Yeah. You had it there. Whatever, whatever two to the 64 is. There we are.
There's the number. I don't know how to say that number, but it's like a million trillion times
nine or something. Yeah. you would have trouble with, you could use a, you could use a,
functional validator. You could find a way around it if you had to, but yeah, I think that's a price
worth, worth paying for the fact that we can do internet integer, stuff really quickly. Right.
And we can do bounds checks much more, much more quickly. yeah. And we have, obviously we have
nice errors in there. If you do pass in something bigger than that, or if you pass, float inf,
uh, again, we'll get infinity same as we would, if you've got a number above that or float nan,
again, you'll get it. You know, that's not allowed. So those cases are all taken care of and they give
you a nice error and there would be an escape, escape hatch if you really had to. Right. So the escape
patch could be, you might write a validator that checks is the number in Python. It checks,
is the number bigger than this limit. If it is race and exception, say number two bigger,
something like that. Yeah. I mean, there isn't actually an escape hatch in the case of Jason,
because we have to do the, we have to do the parsing before we get there. So you'd have to,
you'd have to pass your Jason externally and then pass it in as a Python object and do something weird,
but it's uncommon that you get insanely large numbers like this. I think that the insanely large
numbers like that come up when people try and break things almost. Yeah. They try to break things or
they're trying to do some, some odd math problem where like, I'm trying to use recursion to compute
and see how many prime, you know, something like that. But in the general day to day of I'm accepting
like user input over an API, you know. What I would say is that as a Unix
timestamp in milliseconds is beyond 999 in years, right? It's, it's beyond the date that anyone's ever
going to want to use. So I'm, I don't see that being a problem really. I don't either.
Actually, I think there's probably, I don't know how to make it happen, but there's probably some
interesting performance story for Python getting faster. If it could work with real numerical
types rather than these super flexible numerical types, you know, a lot of times you'll see examples
of math and it's like, well, okay, this, this, you know, pie, long object thing, instead of working
just with, you know, true ints and floats and stuff really slows it down. So I don't know. I, I see a
future maybe someday where Python actually adopts in these sort of limited types like this potentially,
but isn't that what kind of libraries like number number are doing? They're allowing you to selectively
compile a function without going completely off on a tangent. I think that's one approach.
And there you explicitly say whether it's an, an, an, or, or stuff like that. Right.
I think the other option would be like, you know, another way would be to say,
you don't want to be writing Python at that point, because you want to be able, you want all the tools
available in Rust syntax to allow you to, to say all the stuff you want to be able to say and do
integer overflow nicely. So the other option would be some point there'll be a way to basically write
Rust even more easily than now inside Python. Those of us who are using PyCharm and are, you know,
really lucky that we get PyCharm and we can get basically syntax highlighting in any random string.
That doesn't seem too crazy. and obviously even more so if you were porting a file. So I think there are
lots of ways around it. yeah. Yeah. We'll see. Maybe more stuff to come together with the
WebAssembly future. Who knows? Anyway, a lot, a lot of stuff to think about. I think this is this,
I didn't bring this up because I feel like this is a problem or anything. I brought it up just because
I wanted people to be maybe aware that there are some slightly different data types at play here.
Yeah. Since it's going through Rust. Yeah. I think it's important to see that under the
hood we are doing. Yeah. Yeah, exactly. And, and so I've, we can talk about date, time, date, time,
time, time delta validation, but I've built a library in Rust for doing that.
a bit faster than all the ones I could find that is for me, makes the right compromise called
speed date. and that, that is having to deal with exactly those overflow problems. And I, I fuzz
this library a great deal and found a whole bunch of overflow issues by fuzzing it because yeah,
when you're doing raw parsing in, in Rust, you have to think about that stuff that those of us who come
come from a Python background haven't even thought about, you know, the idea that adding two numbers
is scary and might result in a panic is, is, is, is. Yeah. I honestly, I hadn't thought about it for
while. It's kind of nice to just not have to worry about those things. You used to just always have to
consider, you know, is it, is it okay to add, is it okay to multiply these things? Because even if that's
just an intermediate value, something insane might happen along the way. Right. Yeah. Cool. All right.
Well, thank you so much for working on Pydantic putting out there. I know it's made my code
and my projects much nicer. 72,000 other people agree. It seems like. No problem. Thank you very
much. And thank you so much to all of the people who, who help with Pydantic in, in every way from,
from like Eric and Sebastian and people who, who work, like work on it quite a lot, but also to like
all the people who create issues and, and submit one pull request that like makes my job a lot more
fun that it's not just me sitting in a, sitting in an ivory tower, doing it on my own.
It's much more fun to work on projects with people. Absolutely.
Magnus says, thanks to the great show and all the work on Pydantic. Looking forward to Pydantic too.
Right on. Now, before we get out of here, final two questions. If you're going to write some Python
code, work on Pydantic, what editor do you pull up? I pull up PyCharm. I'm a complete convert. I
completely rely on it. Yeah. Right on. And notable IPI or even a cargo package, I suppose, whatever,
whatever you want to shout out to some external library out there that you think is pretty cool.
It's not going to be, it's not going to be particularly interesting because we've talked
about it already, but Py03, I'm like forever impressed by what those guys have done. And
obviously they've made what I'm working on here possible. And they've been really helpful for
me when I've asked dumb Rust questions. So yeah, thank you to them. And yeah, if you're ever thinking
about getting into Rust, doing it from Python is a really, really neat way where when you can't
work out what the hell's going on, you can kind of fall back to Python sometimes.
There's an audience question a while back about any resources that you might recommend
for learning Rust or on the journey to getting to Py03 and so on.
No, I'm like, people always ask me, what, how did I learn to code and where did I do it? And I
basically smashed my head against the wall until it compiled.
Yeah, I hear that. That's a pretty common way. Okay. Final call to action. People are
interested, excited. They have feedback, something like that. They want to try out
Pydantic 2. Particularly if you're using an unusual environment, install Pydantic Core right now.
PIP install Pydantic Core and just run the simple example as one, for example, on the release.
Check it compiles. And if you find an environment where it doesn't work, so it not compiles, but runs,
let me know because that'll be easier to fix sooner rather than later. And then most of all,
once we get to the betas and alphas of Pydantic V2, please come and try it then because,
again, it'll be a lot easier to fix it before it's released and after.
Yeah, absolutely.
And I'll do a lot of shouting on Twitter about that when the time comes.
Perfect. All right, Samuel, thank you so much for being here.
Thank you very much, Michael. It's been a pleasure.
Yeah, you bet. As always. See you later.
Cheers. Bye bye.
This has been another episode of Talk Python to Me.
Thank you to our sponsors. Be sure to check out what they're offering. It really helps support the show.
Listen to an episode of Compiler, an original podcast from Red Hat. Compiler unravels industry topics,
trends, and things you've always wanted to know about tech,
through interviews with the people who know it best. Subscribe today by following talkpython.fm/compiler.
Starting a business is hard. Microsoft for startups, Founders Hub provides all founders at any stage
free resources and connections to solve startup challenges. Apply for free today at talkpython.fm/foundershub.
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.