Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork2.2k
Make sure the type reference is removed from the seen references#11143
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
cloudflare-workers-and-pagesbot commentedDec 18, 2024 • 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.
Deploying pydantic-docs with |
Latest commit: | 75241c2 |
Status: | ✅ Deploy successful! |
Preview URL: | https://95df4efe.pydantic-docs.pages.dev |
Branch Preview URL: | https://11024.pydantic-docs.pages.dev |
codspeed-hqbot commentedDec 18, 2024 • 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#11143 willimprove performances by 6.26%Comparing Summary
Benchmarks breakdown
|
github-actionsbot commentedDec 18, 2024 • 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.
Viicos commentedDec 18, 2024 • 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 note, the current implementation is confusing and I think this is what led to the oversight. Here is a simpler implementation: _generic_recursion_cache:ContextVar[set[str]]=ContextVar('_generic_recursion_cache',default=set())@contextmanagerdefgeneric_recursion_self_type(origin:type[BaseModel],args:tuple[Any, ...])->Iterator[PydanticRecursiveRef|None]:"""This contextmanager should be placed around the recursive calls used to build a generic type, and accept as arguments the generic origin type and the type arguments being passed to it. If the same origin and arguments are observed twice, it implies that a self-reference placeholder can be used while building the core schema, and will produce a schema_ref that will be valid in the final parent schema. """previously_seen_type_refs=_generic_recursion_cache.get()type_ref=get_type_ref(origin,args_override=args)iftype_refinpreviously_seen_type_refs:self_type=PydanticRecursiveRef(type_ref=type_ref)yieldself_typeelse:try:previously_seen_type_refs.add(type_ref)yieldfinally:previously_seen_type_refs.remove(type_ref) Diffdiff --git a/pydantic/_internal/_generics.py b/pydantic/_internal/_generics.pyindex 7b57fc71c..dc474861a 100644--- a/pydantic/_internal/_generics.py+++ b/pydantic/_internal/_generics.py@@ -401,7 +401,7 @@ def check_parameters_count(cls: type[BaseModel], parameters: tuple[Any, ...]) -> raise TypeError(f'Too {description} parameters for {cls}; actual {actual}, expected {expected}')-_generic_recursion_cache: ContextVar[set[str] | None] = ContextVar('_generic_recursion_cache', default=None)+_generic_recursion_cache: ContextVar[set[str]] = ContextVar('_generic_recursion_cache', default=set()) @contextmanager@@ -416,24 +416,17 @@ def generic_recursion_self_type( final parent schema. """ previously_seen_type_refs = _generic_recursion_cache.get()- if previously_seen_type_refs is None:- previously_seen_type_refs = set()- token = _generic_recursion_cache.set(previously_seen_type_refs)- else:- token = None+ type_ref = get_type_ref(origin, args_override=args)- try:- type_ref = get_type_ref(origin, args_override=args)- if type_ref in previously_seen_type_refs:- self_type = PydanticRecursiveRef(type_ref=type_ref)- yield self_type- else:+ if type_ref in previously_seen_type_refs:+ self_type = PydanticRecursiveRef(type_ref=type_ref)+ yield self_type+ else:+ try: previously_seen_type_refs.add(type_ref) yield+ finally: previously_seen_type_refs.remove(type_ref)- finally:- if token:- _generic_recursion_cache.reset(token) def recursively_defined_type_refs() -> set[str]: Edit: turns out this is not going to work well as context variables shouldn't have mutable defaults. |
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.
Nice work, easy enough fix. Thanks for the thorough test.
When parametrizing generic models, the `generic_recursion_self_type`context manager is entered, and is used to avoid infinite recursionsif the same generic model happens to be parametrized again with thesame args during the first parametrization.However, upon exiting the context manager (and thus when the parametrizedmodel is fully created), we forgot to remove the type ref from the set.This happened only if we were already in the process of parametrizinganother model, as otherwise the `_generic_recursion_cache` wouldbe reset (see the `if token` condition).In theory, this couldn't cause issues because parametrized models arecached, and the cache is checked *before* entering the context manager.However, because we have custom `mro()` implementation on the `BaseModel`metaclass, this ends up causing issues is some really specific scenarios.
Well credits goes to@kc0506 for this one |
488aa52
intomainUh oh!
There was an error while loading.Please reload this page.
)When parametrizing generic models, the `generic_recursion_self_type`context manager is entered, and is used to avoid infinite recursionsif the same generic model happens to be parametrized again with thesame args during the first parametrization.However, upon exiting the context manager (and thus when the parametrizedmodel is fully created), we forgot to remove the type ref from the set.This happened only if we were already in the process of parametrizinganother model, as otherwise the `_generic_recursion_cache` wouldbe reset (see the `if token` condition).In theory, this couldn't cause issues because parametrized models arecached, and the cache is checked *before* entering the context manager.However, because we have custom `mro()` implementation on the `BaseModel`metaclass, this ends up causing issues is some really specific scenarios.Co-authored-by: kc0506 <kchong0506@gmail.com>
Uh oh!
There was an error while loading.Please reload this page.
As per#11037.Fixes#11024.
When parametrizing generic models, the
generic_recursion_self_type
context manager is entered, and is used to avoid infinite recursions if the same generic model happens to be parametrized again with the same args during the first parametrization.However, upon exiting the context manager (and thus when the parametrized model is fully created), we forgot to remove the type ref from the set. This happened only if we were already in the process of parametrizing another model, as otherwise the
_generic_recursion_cache
would be reset (see theif token
condition).In theory, this couldn't cause issues because parametrized models are cached, and the cache is checkedbefore entering the context manager. However, because we have custom
mro()
implementation on theBaseModel
metaclass, this ends up causing issues is some really specific scenarios.Change Summary
Related issue number
Checklist