Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork2.2k
Reuse cached core schemas for parametrized generic Pydantic models#11434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
github-actionsbot commentedFeb 12, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Coverage reportClick to see where and how coverage changed
This report was generated bypython-coverage-comment-action |
codspeed-hqbot commentedFeb 12, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
CodSpeed Performance ReportMerging#11434 willimprove performances by 67.14%Comparing Summary
Benchmarks breakdown
|
# Due to the way generic classes are built, it's possible that an invalid schema may be temporarily | ||
# set on generic classes. Probably we could resolve this to ensure that we get proper schema caching | ||
# for generics, but for simplicity for now, we just always rebuild if the class has a generic origin: |
MarkusSintonenFeb 12, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This is pretty weird stuff here... All tests pass fine. Looking at git blame this has been copied over through different refactorings. Most likely not a relevant thing anymore as everything works fine...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Makes me a bit nervous, but given you are probably one of the heaviest users of pydantic generics, my guess is that if you aren't seeing bugs, it's probably working okay? 😅 Admittedly this code has gotten a lot of refactoring since I was last looking at it heavily
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I can do some integration testing to stuff on our end. But I think the existing unit tests around here cover everything already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
An integration test on your end would be great 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Given the example I shared below I'm a little more confident it's safe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
An integration test on your end would be great 👍
Run the tests on our end and everything works. Did it on 2.10.6 with this change on top.
please review |
sydney-runkle commentedFeb 12, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Woah, super small diff, incredible perf boost. We need to look into consequences here, but all tests passing seems quite promising. I've pinged@dmontagu to double check this (he did a lot of the initial work on generics), and@Viicos will also probably want to have a look. I'm running third party tests to see if any integrations fail. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I'll note that if this were going to introduce any issues, it would be with complex recursive generics
Third party tests passing seems like a promising start. |
dmontagu commentedFeb 12, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I'll just share that I wrote an (in-my-opinion) complex recursive generic and tried to get validation to pass when it should not, and was not able to: from __future__importannotationsfrompydanticimportBaseModelfromtypingimportTypeVar,GenericT=TypeVar('T')S=TypeVar('S')classA(BaseModel,Generic[T,S]):t_a:T|None=Nones_a:S|None=Noneb:B[T,S]|None=NoneclassB(BaseModel,Generic[T,S]):t_b:T|None=Nones_b:S|None=Nonec:C[T,S]|None=NoneclassC(BaseModel,Generic[T,S]):t_c:T|None=Nones_c:S|None=Nonea:A[T,S]|None=Nonea1=A[B[int,str],C[bool,float]](t_a=B(t_b=2,s_b='b',c=C(t_c=2,s_c='b')),s_a=C(t_c=True,s_c=1.0,a=A(t_a=True,s_a=1.0)),)b1=B[B[int,str],C[bool,float]](t_b=a1.t_a,s_b=a1.s_a,c=C[B[int,str],C[bool,float]](a=a1))data= {'t_b': {'t_b':2,'s_b':'b','c': {'t_c':2,'s_c':'b','a':None}},'s_b': {'t_c':True,'s_c':1.0,'a': {'t_a':True,'s_a':1.0,'b':None}},'c': {'t_c':None,'s_c':None,'a': {'t_a': {'t_b':2,'s_b':'b','c': {'t_c':2,'s_c':'b','a':None}},'s_a': {'t_c':True,'s_c':1.0,'a': {'t_a':True,'s_a':1.0,'b':None}},'b':None, }, },}b1.model_validate(data) If I edited any of the stuff in So I feel like whatever concerns I had might have been mitigated by improvements to our generics handling in the time since I was responsible for it 😅. |
sydney-runkle left a comment• edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Given the above test passing, and all of our current tests, and all of our third party tests, I'm approving this. Great find@MarkusSintonen!! This will pair quite well with our other performance improvements in v2.11.
I'll wait for@Viicos to approve as well.
dmontagu commentedFeb 12, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@MarkusSintonen I'm curious how much of a startup speed improvement you noticed on your own codebase. Was it significantly more/less than the codspeed benchmark here, or comparable? (Basically trying to get an intuition for how representative this codspeed example is of your usage patterns.) |
this is awesome. Thank you@MarkusSintonen. |
definition_reference_schemas
without reconstructingdefinition_reference_schemas
without reconstructingMarkusSintonen commentedFeb 13, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
It is quite close to what we see here from codspeed. I got about +50-60% in the full initialization time of the whole FastAPI stack utilizing generics with different complexities. |
@dmontagu do you think it would make sense to add your test as part of the unit tests here if you are unsure if the case is covered? |
Yes, let's! |
I think |
2f3e1ac
intopydantic:mainUh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Change Summary
Speeds up generics initialization significantly in the codspeed benchmark (+67.4%).
Related issue number
Many of the model init slowness related issues eg.#6768
Checklist