-
-
Notifications
You must be signed in to change notification settings - Fork 517
Added dynamic class hook for from_queryset manager
#427
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
Added dynamic class hook for from_queryset manager
#427
Conversation
| raise NotImplementedError | ||
|
|
||
| class DynamicClassFromMethodCallback(DynamicClassPluginCallback): | ||
|
|
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.
Add class attribute callee here, it should be certain that it's a MemberExpr. Also, add assert with the type in the __init__ so that it would fail if this subclass is used incorrectly. It will allow a bit nicer autocompletion and maybe will save you an assert somewhere in subclasses.
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.
Adding an assert in __init__ makes like no sense, we don't have DynamicClassDefContext available up there (only NewSemanalDjangoPlugin instance passed in ManagerFromQuerySetCallback(self) in get_dynamic_class_hook. I will add an assert in __call__ though :)
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.
Yes, in __call__, my bad:)
|
|
||
| method_type = method_node.type | ||
| if not isinstance(method_type, CallableType): | ||
| if not self.semanal_api.final_iteration: |
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.
defer_till_next_iteration too?
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.
@mkurnikov will if not isinstance(method_type, CallableType) and not self.defer_till_next_iteration() work? I'm not 100% sure if I get it right how deferring works
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.
If it's not isinstance(CallableType), which type it is? Placeholder?
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.
It might be Overloaded, CallableType or None actually
| method_node.arguments[1:]): | ||
| bound_arg_type = self.semanal_api.anal_type(arg_type, allow_placeholder=True) | ||
| if bound_arg_type is None and not self.semanal_api.final_iteration: | ||
| self.semanal_api.defer() |
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.
Same thing as above, defer_till_next_iteration or/and exception raise, instead of just return.
|
|
||
| base_manager_info = self.callee.expr.node | ||
|
|
||
| if base_manager_info is None and not self.defer_till_next_iteration(reason='base_manager_info is None'): |
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 think this reason is useful. base_manager_info is None means something, it should be added as the reason.
How could it happen that TypeInfo is None here?
Also, let's see what type self.callee.expr is. It's an expression on the left side of the a.b. It could be get_manager_class().from_queryset() (which will be CallExpr as self.callee.expr, not a RefExpr), so there's no node attribute available, and so forth.
If it's not a RefExpr, a good idea would be to fallback to Any here.
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.
@kszmigiel I still want you to add proper error string here with an explanation of the conditions of why it could be None.
mypy_django_plugin/lib/helpers.py
Outdated
| return | ||
|
|
||
| method_type: CallableType = method_node.type | ||
| method_type: CallableType = method_node.type # type: ignore |
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.
cast maybe?
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.
Applied, works, thanks for the tip 👍
| object_history_template: str = ... | ||
| popup_response_template: str = ... | ||
| actions: Sequence[Callable[[ModelAdmin, HttpRequest, QuerySet], None]] = ... | ||
| actions: Sequence[Union[Callable[[ModelAdmin, HttpRequest, QuerySet], None], str]] = ... |
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 guess that this PR has unrelated changes from other PRs. Is that the case, @kszmigiel?
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.
Yes, I've updated stub files to the master version, I can revert it if needed (this commit: b032a3d)
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.
The proper way to do it is to rebase this PR based on current master
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.
👍 It'll be quite a challenge for me, but I'll try
sobolevn
left a comment
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! 👍
Can we merge it? @kszmigiel
|
@kszmigiel looks like it is passing now. |
|
@sobolevn I think we're ready to merge, but I would like to hear @mkurnikov opinion first. I know there are a few fragments I need to cleanup, but I think I can do this along with writing docs and comments after the rebase to master. BTW how about getting readthedocs.io page for docs? I can take care of writing the docs as the task for next week - we lack a lot of them, and I could use a short break from coding too. |
|
|
||
| assert ctx.call.callee is not None | ||
| if not isinstance(ctx.call.callee, MemberExpr): | ||
| # throw error? |
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.
Add assert here, instead of isinstance+return.
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.
Done, I will include this in next PR
|
|
||
| base_manager_info = self.callee.expr.node | ||
|
|
||
| if base_manager_info is None and not self.defer_till_next_iteration(reason='base_manager_info is None'): |
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.
@kszmigiel I still want you to add proper error string here with an explanation of the conditions of why it could be None.
| @@ -0,0 +1,236 @@ | |||
| - case: from_queryset_with_base_manager | |||
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.
According to the test, there has to be some kind of magic somewhere which adds MyModel as a generic argument for NewManager. Could we get rid of that?
So, what I'm trying to say is that tests are a bit wrong. from_queryset line should be
class ModelQuerySet(models.QuerySet[MyModel]):
...
NewManager = BaseManager[MyModel].from_queryset(ModelQuerySet)
and current line should error out with Need type annotation for like it's done here https://github.com/typeddjango/django-stubs/blob/newsemanal-progress/test-data/typecheck/managers/test_managers.yml#L128
and NewManager in the current setup should give Any as a generic argument, see https://github.com/typeddjango/django-stubs/blob/newsemanal-progress/test-data/typecheck/managers/test_managers.yml#L106
Could be done in a separate PR though.
|
🎉 |
No description provided.