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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Basic fix of GenericModel cache to detect order of arguments in Union models #4482
Conversation
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.
otherwise LGTM, you just need to add "fix " to the issue ID in the PR body to satisfy the checks and also allow github to auto close the issue.
pydantic/generics.py
Outdated
def _cache_key( | ||
_params: Union[Type[Any], Tuple[Type[Any], ...]] | ||
) -> Tuple[Type[GenericModelT], Union[Type[Any], Tuple[Type[Any], ...]], Tuple[Any, ...]]: |
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.
Just use, this is an internal method, so that's sufficient.
def _cache_key( | |
_params: Union[Type[Any], Tuple[Type[Any], ...]] | |
) -> Tuple[Type[GenericModelT], Union[Type[Any], Tuple[Type[Any], ...]], Tuple[Any, ...]]: | |
def _cache_key(_params: Any) -> Tuple[Type[GenericModelT], Any, Tuple[Any, ...]]: |
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.
Ah! Tried to replace with Type[Any]
instead of Any
but that did not work.
Also, I was wondering about closing the issue, as the nested variant of the issue is not fixed. Do you want to keep the issue open for this or is the documented and skipped test good enough?
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 don't mind, if you want to keep the issue open, use the PR ID in the change file.
Either way this is urgent since people are (understandably) waiting on the patch release.
I just want this to pass so I can merge it and get the release done.
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.
Added 'fix'. You can reopen the issue after, if needed.
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.
great, thanks.
LGTM. @PrettyWood please review, you happy with this? |
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.
LGTM! Thanks
Thanks so much @sveinugu. |
You're welcome! Happy to get the fix in. And thank you for creating such a useful library! Looking forward to v2. |
We recently bumped Pydantic from 1.10.1 to 1.10.2 and our tests started failing. After some lengthy investigation I found this change to be the root cause. I'd like to ask you for your opinions. We have a Pydantic model with a field such as f: MyGenericModel[Callable[[Task, Dict[str, Any]], Iterable[Result]]] = Field(...) Just importing Python module with this code gives me:
The problem seems to be that the function def get_args(tp):
"""Get type arguments with all substitutions performed.
For unions, basic simplifications used by Union constructor are performed.
Examples::
get_args(Dict[str, int]) == (str, int)
get_args(int) == ()
get_args(Union[int, Union[T, int], str][int]) == (int, str)
get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
get_args(Callable[[], T][int]) == ([], int)
"""
if isinstance(tp, _GenericAlias) and not tp._special:
res = tp.__args__
if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
res = (list(res[:-1]), res[-1]) # <======== this list is a problem
return res
return () If I skip the callable typing and only define the field as f: MyGenericModel[Callable] = Field(...) then it runs okay. Thanks in advance! |
@pepastach Hmm... Seems we did not think of that. Surprised that typing uses lists and not tuples for Callable arguments, but there is probably a good reason for this (or it might be just that it would be too much a nuisance to switch between brackets and parentheses...). In any case, there might be other examples of unhashable return values from I am new as contributor to pydantic, but I assume it would be cleanest if you could create another issue for this. |
Yes, new issue which references this PR. But honestly, generics are one of the things that needs to be refactored properly in V2. This patches-on-patches-on-patches are getting more and more complex - no one's fault, but I really hope we can get away from some of these anti-patterns in V2. Anyway, apart from me whinging, this shouldn't be too hard to fix/work around as @sveinugu suggests. |
I've creatd #4551 |
Change Summary
Basic fix of GenericModel cache to include the order of arguments in Union models into the cache key.
This fixes the basic problem described in issue #4474, so that e.g. Model[Union[float, int]] is not used as a
blueprint for Model[Union[float, int]]. Added tests for basic and nested versions of the problem. Fixing the
nested problem depends on the fixing of the almost identical issue described in
python/cpython#86483 in CPython. The test was thus skipped, but kept for
documentation.
Related issue number
fix #4474
Checklist
changes/<pull request or issue id>-<github username>.md
file added describing change(see changes/README.md for details.
You can skip this check if the change does not need a change file.)