-
-
Notifications
You must be signed in to change notification settings - Fork 493
create and re-use TypeAlias
es and TypeVar
s for "user" and "any user"
#2384
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
create and re-use TypeAlias
es and TypeVar
s for "user" and "any user"
#2384
Conversation
08131cc
to
513df00
Compare
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.
Generally - looks great! Just one note
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 we should change the base class to be AbstractUser
. This is a section of what the "Specifying a custom user model" says in the docs:
[...] The easiest way to construct a compliant custom user model is to inherit from AbstractBaseUser. AbstractBaseUser provides the core implementation of a user model, including hashed passwords and tokenized password resets. [...]
https://docs.djangoproject.com/en/5.1/topics/auth/customizing/#specifying-a-custom-user-model
As far as I understand, this is not the base class, this is the default type, unless |
That's interesting that they suggest that... The issue is that It does look like there's probably some complications if we move the base class up (subclasses of |
513df00
to
06a4f41
Compare
I rolled back the change since it does affect a user who subclasses This change still adds some helpful aliases so it can get merged in and the follow-up can be done independently. |
AUTH_USER_MODEL
to be AbstractUser
and reuse aliasesTypeAlias
es and TypeVar
s for User and AnyUser
TypeAlias
es and TypeVar
s for User and AnyUserTypeAlias
es and TypeVar
s for "user" and "any user"
bf15c5b
to
2697324
Compare
This change also adds a few user type vars and alises to cover the common use cases of ``User``, ``User | AnonymousUser``, and their ``TypeVar`` forms for using in generic contexts.
2697324
to
e29f60b
Compare
@sobolevn any chance I can get a re-review? I updated this to fix the conflicts, but it was already updated to remove the concern about shifting the base user. It now only provides a few useful type aliases that can be shared across the codebase. High level changes:
|
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.
Thanks for the ping! Mostly looks good.
def get_user_model() -> type[_UserModel]: ... | ||
def get_user(request: HttpRequest | Client) -> _UserModel | AnonymousUser: ... | ||
async def aget_user(request: HttpRequest | Client) -> _UserModel | AnonymousUser: ... | ||
def get_user_model() -> type[_User]: ... |
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.
def get_user_model() -> type[_User]: ... | |
def get_user_model() -> UserModel: ... |
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 look to see if there are any type[_User]
that can be replaced with the UserModel
alias
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 think there was just one other that you didn't mention
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 suggestion seems reasonable, but a few comments and I just want to understand what you'd prefer before continuing.
def get_user_model() -> type[_UserModel]: ... | ||
def get_user(request: HttpRequest | Client) -> _UserModel | AnonymousUser: ... | ||
async def aget_user(request: HttpRequest | Client) -> _UserModel | AnonymousUser: ... | ||
def get_user_model() -> type[_User]: ... |
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 look to see if there are any type[_User]
that can be replaced with the UserModel
alias
Yes, I prefer re-exporting over re-definition, because definition can change at some point :) |
(missclick, sorry) |
Ruff really doesn't like re-exporting with a rename, but I think I finally got it to stop deleting the re-exports 😒 |
202711d
to
fb5379a
Compare
updated |
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.
Thanks!
# do not use the alias `_User` so the bound remains at `AbstractUser` | ||
_UserType = TypeVar("_UserType", bound=AbstractUser) |
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 bound seems incorrect -- or at least it breaks the usage in sentry
the docs for django seem to indicate AbstractBaseUser as the bound 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.
yes, this probably is more correct. thanks for noticing!
btw, can we test compat with sentry somehow? like running mypy-primer
but for django
-related projects only? 🤔
it should be possible if projects that you are talking about are opensource.
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.
yep yep -- the repo in question is fully open source https://github.com/getsentry/sentry
there's a few problems that might be showstoppers though:
- we've not completely made it through our blocklist of files: https://github.com/getsentry/sentry/blob/f7c3a817a586f9e5807784e0deb2db1b634e919b/pyproject.toml#L114
- we're carrying a patch which undoes abstract
objects = ...
: getsentry@f96ab51 - we're carrying a set of patches because we (ab)use
cache
versions as strings: getsentry@28d1e15 getsentry@3c73550
so I guess as long as it doesn't have to typecheck completely cleanly it should be 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.
created #2645 for the fix
@@ -114,28 +112,26 @@ class PasswordResetForm(forms.Form): | |||
extra_email_context: dict[str, str] | None = ..., | |||
) -> None: ... | |||
|
|||
class SetPasswordForm(forms.Form): | |||
error_messages: _ErrorMessagesDict | |||
class SetPasswordForm(Generic[_UserType], SetPasswordMixin, forms.Form): |
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 not generic at runtime, which prevents this from being subclassed.
class MySetPasswordForm(SetPasswordForm):
pass
mypy complains: error: Missing type parameters for generic type "SetPasswordForm" [type-arg]
class MySetPasswordForm(SetPasswordForm[User]):
pass
Fails at runtime: TypeError: 'DeclarativeFieldsMetaclass' object is not subscriptable
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.
@andersk can you please send a PR to ext/
? So we can monkeypatch this object in runtime.
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.
Maybe we should add a small test to ensure that ? Something parsing pyi file for ``generic` calls and ensuring they match the one declared in the ext ? I can try something like that
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.
Sure, this would be a great addition. I have no idea on how to actually do that :)
Maybe a stubtest
extra check? 🤔
I can open an issue in mypy.
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.
Would be nice if stubtest can catch that indeed
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 have made things!
This change also adds a few user type vars and alises to cover the common use cases of
User
,User | AnonymousUser
, and theirTypeVar
forms for using in generic contexts.