From 513df00963a06a6143b2453bebcfec3130e0dfff Mon Sep 17 00:00:00 2001 From: "Terence D. Honles" Date: Thu, 26 Sep 2024 16:53:51 -0700 Subject: [PATCH] update default ``AUTH_USER_MODEL`` to be ``AbstractUser`` and reuse aliases This change updates the previous ``AUTH_USER_MODEL`` placeholder alias to default to ``AbstractUser`` instead of ``AbstractBaseUser`` which does not include the ``PermissionsMixin``, so that the type alias works better even without the mypy plugin. 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. --- django-stubs/contrib/auth/__init__.pyi | 23 +++++------ django-stubs/contrib/auth/backends.pyi | 20 ++++------ django-stubs/contrib/auth/base_user.pyi | 6 --- django-stubs/contrib/auth/decorators.pyi | 5 +-- django-stubs/contrib/auth/forms.pyi | 40 +++++++++---------- django-stubs/contrib/auth/middleware.pyi | 7 ++-- django-stubs/contrib/auth/models.pyi | 25 ++++++++---- .../contrib/auth/password_validation.pyi | 16 ++++---- django-stubs/contrib/auth/tokens.pyi | 10 ++--- django-stubs/contrib/auth/views.pyi | 6 +-- django-stubs/http/request.pyi | 7 ++-- django-stubs/test/client.pyi | 6 +-- mypy_django_plugin/main.py | 4 +- 13 files changed, 84 insertions(+), 91 deletions(-) diff --git a/django-stubs/contrib/auth/__init__.pyi b/django-stubs/contrib/auth/__init__.pyi index 1e9413413..5d597e7e8 100644 --- a/django-stubs/contrib/auth/__init__.pyi +++ b/django-stubs/contrib/auth/__init__.pyi @@ -1,8 +1,7 @@ from typing import Any from django.contrib.auth.backends import BaseBackend -from django.contrib.auth.base_user import _UserModel -from django.contrib.auth.models import AnonymousUser +from django.contrib.auth.models import _AnyUser, _User from django.db.models.options import Options from django.http.request import HttpRequest from django.test.client import Client @@ -18,17 +17,15 @@ REDIRECT_FIELD_NAME: str def load_backend(path: str) -> BaseBackend: ... def get_backends() -> list[BaseBackend]: ... -def authenticate(request: HttpRequest | None = ..., **credentials: Any) -> _UserModel | None: ... -async def aauthenticate(request: HttpRequest | None = ..., **credentials: Any) -> _UserModel | None: ... -def login(request: HttpRequest, user: _UserModel | None, backend: type[BaseBackend] | str | None = ...) -> None: ... -async def alogin( - request: HttpRequest, user: _UserModel | None, backend: type[BaseBackend] | str | None = ... -) -> None: ... +def authenticate(request: HttpRequest | None = ..., **credentials: Any) -> _User | None: ... +async def aauthenticate(request: HttpRequest | None = ..., **credentials: Any) -> _User | None: ... +def login(request: HttpRequest, user: _User | None, backend: type[BaseBackend] | str | None = ...) -> None: ... +async def alogin(request: HttpRequest, user: _User | None, backend: type[BaseBackend] | str | None = ...) -> None: ... def logout(request: HttpRequest) -> None: ... async def alogout(request: HttpRequest) -> None: ... -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]: ... +def get_user(request: HttpRequest | Client) -> _AnyUser: ... +async def aget_user(request: HttpRequest | Client) -> _AnyUser: ... def get_permission_codename(action: str, opts: Options) -> str: ... -def update_session_auth_hash(request: HttpRequest, user: _UserModel) -> None: ... -async def aupdate_session_auth_hash(request: HttpRequest, user: _UserModel) -> None: ... +def update_session_auth_hash(request: HttpRequest, user: _User) -> None: ... +async def aupdate_session_auth_hash(request: HttpRequest, user: _User) -> None: ... diff --git a/django-stubs/contrib/auth/backends.pyi b/django-stubs/contrib/auth/backends.pyi index c63628db4..e61261359 100644 --- a/django-stubs/contrib/auth/backends.pyi +++ b/django-stubs/contrib/auth/backends.pyi @@ -1,18 +1,16 @@ -from typing import Any, TypeVar +from typing import Any -from django.contrib.auth.base_user import AbstractBaseUser, _UserModel -from django.contrib.auth.models import AnonymousUser, Permission +from django.contrib.auth.models import Permission, _AnyUser, _User from django.db.models import QuerySet from django.db.models.base import Model from django.http.request import HttpRequest from typing_extensions import TypeAlias -UserModel: TypeAlias = type[_UserModel] -_AnyUser: TypeAlias = _UserModel | AnonymousUser +UserModel: TypeAlias = type[_User] class BaseBackend: - def authenticate(self, request: HttpRequest | None, **kwargs: Any) -> _UserModel | None: ... - def get_user(self, user_id: Any) -> _UserModel | None: ... + def authenticate(self, request: HttpRequest | None, **kwargs: Any) -> _User | None: ... + def get_user(self, user_id: Any) -> _User | None: ... def get_user_permissions(self, user_obj: _AnyUser, obj: Model | None = ...) -> set[str]: ... def get_group_permissions(self, user_obj: _AnyUser, obj: Model | None = ...) -> set[str]: ... def get_all_permissions(self, user_obj: _AnyUser, obj: Model | None = ...) -> set[str]: ... @@ -21,7 +19,7 @@ class BaseBackend: class ModelBackend(BaseBackend): def authenticate( self, request: HttpRequest | None, username: str | None = ..., password: str | None = ..., **kwargs: Any - ) -> _UserModel | None: ... + ) -> _User | None: ... def has_module_perms(self, user_obj: _AnyUser, app_label: str) -> bool: ... def user_can_authenticate(self, user: _AnyUser | None) -> bool: ... def with_perm( @@ -30,15 +28,13 @@ class ModelBackend(BaseBackend): is_active: bool = ..., include_superusers: bool = ..., obj: Model | None = ..., - ) -> QuerySet[_UserModel]: ... + ) -> QuerySet[_User]: ... class AllowAllUsersModelBackend(ModelBackend): ... -_U = TypeVar("_U", bound=AbstractBaseUser) - class RemoteUserBackend(ModelBackend): create_unknown_user: bool def clean_username(self, username: str) -> str: ... - def configure_user(self, request: HttpRequest, user: _U, created: bool = ...) -> _U: ... + def configure_user(self, request: HttpRequest, user: _User, created: bool = ...) -> _User: ... class AllowAllUsersRemoteUserBackend(RemoteUserBackend): ... diff --git a/django-stubs/contrib/auth/base_user.pyi b/django-stubs/contrib/auth/base_user.pyi index a99bdf431..4740dd504 100644 --- a/django-stubs/contrib/auth/base_user.pyi +++ b/django-stubs/contrib/auth/base_user.pyi @@ -4,7 +4,6 @@ from django.db import models from django.db.models.base import Model from django.db.models.expressions import Combinable from django.db.models.fields import BooleanField -from typing_extensions import TypeAlias _T = TypeVar("_T", bound=Model) @@ -42,8 +41,3 @@ class AbstractBaseUser(models.Model): @classmethod @overload def normalize_username(cls, username: Any) -> Any: ... - -# This is our "placeholder" type the mypy plugin refines to configured 'AUTH_USER_MODEL' -# wherever it is used as a type. The most recognised example of this is (probably) -# `HttpRequest.user` -_UserModel: TypeAlias = AbstractBaseUser # noqa: PYI047 diff --git a/django-stubs/contrib/auth/decorators.pyi b/django-stubs/contrib/auth/decorators.pyi index a17f5bd45..6639c14b5 100644 --- a/django-stubs/contrib/auth/decorators.pyi +++ b/django-stubs/contrib/auth/decorators.pyi @@ -1,14 +1,13 @@ from collections.abc import Callable, Iterable from typing import TypeVar, overload -from django.contrib.auth.base_user import _UserModel -from django.contrib.auth.models import AnonymousUser +from django.contrib.auth.models import _AnyUser from django.http.response import HttpResponseBase _VIEW = TypeVar("_VIEW", bound=Callable[..., HttpResponseBase]) def user_passes_test( - test_func: Callable[[_UserModel | AnonymousUser], bool], + test_func: Callable[[_AnyUser], bool], login_url: str | None = ..., redirect_field_name: str | None = ..., ) -> Callable[[_VIEW], _VIEW]: ... diff --git a/django-stubs/contrib/auth/forms.pyi b/django-stubs/contrib/auth/forms.pyi index 37aea5444..9664f704e 100644 --- a/django-stubs/contrib/auth/forms.pyi +++ b/django-stubs/contrib/auth/forms.pyi @@ -1,9 +1,9 @@ from collections.abc import Iterable from logging import Logger -from typing import Any, TypeVar +from typing import Any, Generic from django import forms -from django.contrib.auth.base_user import AbstractBaseUser, _UserModel +from django.contrib.auth.models import _User, _UserType from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.core.exceptions import ValidationError from django.db import models @@ -15,8 +15,7 @@ from typing_extensions import TypeAlias logger: Logger -UserModel: TypeAlias = type[_UserModel] -_User = TypeVar("_User", bound=AbstractBaseUser) +UserModel: TypeAlias = type[_User] class ReadOnlyPasswordHashWidget(forms.Widget): template_name: str @@ -31,18 +30,19 @@ class UsernameField(forms.CharField): def to_python(self, value: Any | None) -> Any | None: ... def widget_attrs(self, widget: Widget) -> dict[str, Any]: ... -class BaseUserCreationForm(forms.ModelForm[_User]): +# mypy is incorrectly interpreting this as not generic, so explicitly say it is +class BaseUserCreationForm(Generic[_UserType], forms.ModelForm[_UserType]): error_messages: _ErrorMessagesDict password1: forms.Field password2: forms.Field def __init__(self, *args: Any, **kwargs: Any) -> None: ... def clean_password2(self) -> str: ... - def save(self, commit: bool = ...) -> _User: ... + def save(self, commit: bool = ...) -> _UserType: ... -class UserCreationForm(BaseUserCreationForm[_User]): +class UserCreationForm(BaseUserCreationForm[_UserType]): def clean_username(self) -> str: ... -class UserChangeForm(forms.ModelForm[_User]): +class UserChangeForm(forms.ModelForm[_UserType]): password: forms.Field def __init__(self, *args: Any, **kwargs: Any) -> None: ... @@ -51,11 +51,11 @@ class AuthenticationForm(forms.Form): password: forms.Field error_messages: _ErrorMessagesDict request: HttpRequest | None - user_cache: _UserModel | None + user_cache: _User | None username_field: models.Field def __init__(self, request: HttpRequest | None = ..., *args: Any, **kwargs: Any) -> None: ... - def confirm_login_allowed(self, user: AbstractBaseUser) -> None: ... - def get_user(self) -> _UserModel: ... + def confirm_login_allowed(self, user: _User) -> None: ... + def get_user(self) -> _User: ... def get_invalid_login_error(self) -> ValidationError: ... def clean(self) -> dict[str, Any]: ... @@ -70,7 +70,7 @@ class PasswordResetForm(forms.Form): to_email: str, html_email_template_name: str | None = ..., ) -> None: ... - def get_users(self, email: str) -> Iterable[_UserModel]: ... + def get_users(self, email: str) -> Iterable[_User]: ... def save( self, domain_override: str | None = ..., @@ -84,28 +84,28 @@ class PasswordResetForm(forms.Form): extra_email_context: dict[str, str] | None = ..., ) -> None: ... -class SetPasswordForm(forms.Form): +class SetPasswordForm(Generic[_UserType], forms.Form): error_messages: _ErrorMessagesDict new_password1: forms.Field new_password2: forms.Field - user: AbstractBaseUser - def __init__(self, user: AbstractBaseUser, *args: Any, **kwargs: Any) -> None: ... + user: _UserType + def __init__(self, user: _UserType, *args: Any, **kwargs: Any) -> None: ... def clean_new_password2(self) -> str: ... - def save(self, commit: bool = ...) -> AbstractBaseUser: ... + def save(self, commit: bool = ...) -> _UserType: ... class PasswordChangeForm(SetPasswordForm): error_messages: _ErrorMessagesDict old_password: forms.Field def clean_old_password(self) -> str: ... -class AdminPasswordChangeForm(forms.Form): +class AdminPasswordChangeForm(Generic[_UserType], forms.Form): error_messages: _ErrorMessagesDict required_css_class: str password1: forms.Field password2: forms.Field - user: AbstractBaseUser - def __init__(self, user: AbstractBaseUser, *args: Any, **kwargs: Any) -> None: ... + user: _UserType + def __init__(self, user: _UserType, *args: Any, **kwargs: Any) -> None: ... def clean_password2(self) -> str: ... - def save(self, commit: bool = ...) -> AbstractBaseUser: ... + def save(self, commit: bool = ...) -> _UserType: ... @property def changed_data(self) -> list[str]: ... diff --git a/django-stubs/contrib/auth/middleware.pyi b/django-stubs/contrib/auth/middleware.pyi index 0465dccf5..74823b721 100644 --- a/django-stubs/contrib/auth/middleware.pyi +++ b/django-stubs/contrib/auth/middleware.pyi @@ -1,10 +1,9 @@ -from django.contrib.auth.base_user import _UserModel -from django.contrib.auth.models import AnonymousUser +from django.contrib.auth.models import _AnyUser from django.http.request import HttpRequest from django.utils.deprecation import MiddlewareMixin -def get_user(request: HttpRequest) -> AnonymousUser | _UserModel: ... -async def auser(request: HttpRequest) -> AnonymousUser | _UserModel: ... +def get_user(request: HttpRequest) -> _AnyUser: ... +async def auser(request: HttpRequest) -> _AnyUser: ... class AuthenticationMiddleware(MiddlewareMixin): def process_request(self, request: HttpRequest) -> None: ... diff --git a/django-stubs/contrib/auth/models.pyi b/django-stubs/contrib/auth/models.pyi index 78702f1e7..ff7243053 100644 --- a/django-stubs/contrib/auth/models.pyi +++ b/django-stubs/contrib/auth/models.pyi @@ -12,9 +12,20 @@ from django.db.models.manager import EmptyManager from django.utils.functional import _StrOrPromise from typing_extensions import Self, TypeAlias -_AnyUser: TypeAlias = Model | AnonymousUser +_AnyUser: TypeAlias = _User | AnonymousUser -def update_last_login(sender: type[AbstractBaseUser], user: AbstractBaseUser, **kwargs: Any) -> None: ... +# This is our "placeholder" type the mypy plugin refines to configured 'AUTH_USER_MODEL' +# wherever it is used as a type. The most recognised example of this is (probably) +# `HttpRequest.user` +_User: TypeAlias = AbstractUser + +# These are only needed for generic classes in order to bind to a specific implementation +_AnyUserType = TypeVar("_AnyUserType", bound=_AnyUser) # noqa: PYI018 + +# do not use the alias `_User` so the bound remains at `AbstractUser` +_UserType = TypeVar("_UserType", bound=AbstractUser) + +def update_last_login(sender: type[_User], user: _User, **kwargs: Any) -> None: ... class PermissionManager(models.Manager[Permission]): def get_by_natural_key(self, codename: str, app_label: str, model: str) -> Permission: ... @@ -38,15 +49,13 @@ class Group(models.Model): permissions = models.ManyToManyField(Permission) def natural_key(self) -> tuple[str]: ... -_T = TypeVar("_T", bound=Model) - -class UserManager(BaseUserManager[_T]): +class UserManager(BaseUserManager[_UserType]): def create_user( self, username: str, email: str | None = ..., password: str | None = ..., **extra_fields: Any - ) -> _T: ... + ) -> _UserType: ... def create_superuser( self, username: str, email: str | None = ..., password: str | None = ..., **extra_fields: Any - ) -> _T: ... + ) -> _UserType: ... def with_perm( self, perm: str | Permission, @@ -54,7 +63,7 @@ class UserManager(BaseUserManager[_T]): include_superusers: bool = ..., backend: str | None = ..., obj: Model | None = ..., - ) -> QuerySet[_T]: ... + ) -> QuerySet[_UserType]: ... class PermissionsMixin(models.Model): is_superuser = models.BooleanField() diff --git a/django-stubs/contrib/auth/password_validation.pyi b/django-stubs/contrib/auth/password_validation.pyi index b9318e934..bafc709c8 100644 --- a/django-stubs/contrib/auth/password_validation.pyi +++ b/django-stubs/contrib/auth/password_validation.pyi @@ -2,20 +2,20 @@ from collections.abc import Mapping, Sequence from pathlib import Path, PosixPath from typing import Any, Protocol, type_check_only -from django.contrib.auth.base_user import _UserModel +from django.contrib.auth.models import _User @type_check_only class PasswordValidator(Protocol): - def validate(self, password: str, user: _UserModel | None = ..., /) -> None: ... + def validate(self, password: str, user: _User | None = ..., /) -> None: ... def get_help_text(self) -> str: ... def get_default_password_validators() -> list[PasswordValidator]: ... def get_password_validators(validator_config: Sequence[Mapping[str, Any]]) -> list[PasswordValidator]: ... def validate_password( - password: str, user: _UserModel | None = ..., password_validators: Sequence[PasswordValidator] | None = ... + password: str, user: _User | None = ..., password_validators: Sequence[PasswordValidator] | None = ... ) -> None: ... def password_changed( - password: str, user: _UserModel | None = ..., password_validators: Sequence[PasswordValidator] | None = ... + password: str, user: _User | None = ..., password_validators: Sequence[PasswordValidator] | None = ... ) -> None: ... def password_validators_help_texts(password_validators: Sequence[PasswordValidator] | None = ...) -> list[str]: ... @@ -24,7 +24,7 @@ password_validators_help_text_html: Any class MinimumLengthValidator: min_length: int def __init__(self, min_length: int = ...) -> None: ... - def validate(self, password: str, user: _UserModel | None = ...) -> None: ... + def validate(self, password: str, user: _User | None = ...) -> None: ... def get_help_text(self) -> str: ... class UserAttributeSimilarityValidator: @@ -32,16 +32,16 @@ class UserAttributeSimilarityValidator: user_attributes: Sequence[str] max_similarity: float def __init__(self, user_attributes: Sequence[str] = ..., max_similarity: float = ...) -> None: ... - def validate(self, password: str, user: _UserModel | None = ...) -> None: ... + def validate(self, password: str, user: _User | None = ...) -> None: ... def get_help_text(self) -> str: ... class CommonPasswordValidator: DEFAULT_PASSWORD_LIST_PATH: Path passwords: set[str] def __init__(self, password_list_path: Path | PosixPath | str = ...) -> None: ... - def validate(self, password: str, user: _UserModel | None = ...) -> None: ... + def validate(self, password: str, user: _User | None = ...) -> None: ... def get_help_text(self) -> str: ... class NumericPasswordValidator: - def validate(self, password: str, user: _UserModel | None = ...) -> None: ... + def validate(self, password: str, user: _User | None = ...) -> None: ... def get_help_text(self) -> str: ... diff --git a/django-stubs/contrib/auth/tokens.pyi b/django-stubs/contrib/auth/tokens.pyi index 05f05b5e0..8fd5a0498 100644 --- a/django-stubs/contrib/auth/tokens.pyi +++ b/django-stubs/contrib/auth/tokens.pyi @@ -1,17 +1,17 @@ from datetime import date, datetime from typing import Any -from django.contrib.auth.base_user import _UserModel +from django.contrib.auth.models import _User class PasswordResetTokenGenerator: key_salt: str secret: str | bytes secret_fallbacks: list[str | bytes] algorithm: str - def make_token(self, user: _UserModel) -> str: ... - def check_token(self, user: _UserModel | None, token: str | None) -> bool: ... - def _make_token_with_timestamp(self, user: _UserModel, timestamp: int, secret: str | bytes = ...) -> str: ... - def _make_hash_value(self, user: _UserModel, timestamp: int) -> str: ... + def make_token(self, user: _User) -> str: ... + def check_token(self, user: _User | None, token: str | None) -> bool: ... + def _make_token_with_timestamp(self, user: _User, timestamp: int, secret: str | bytes = ...) -> str: ... + def _make_hash_value(self, user: _User, timestamp: int) -> str: ... def _num_seconds(self, dt: datetime | date) -> int: ... def _now(self) -> datetime: ... diff --git a/django-stubs/contrib/auth/views.pyi b/django-stubs/contrib/auth/views.pyi index 443a7855a..ce5c0d977 100644 --- a/django-stubs/contrib/auth/views.pyi +++ b/django-stubs/contrib/auth/views.pyi @@ -1,14 +1,14 @@ from typing import Any -from django.contrib.auth.base_user import _UserModel from django.contrib.auth.forms import AuthenticationForm +from django.contrib.auth.models import _User from django.http.request import HttpRequest from django.http.response import HttpResponse, HttpResponseRedirect from django.views.generic.base import TemplateView from django.views.generic.edit import FormView from typing_extensions import TypeAlias -UserModel: TypeAlias = type[_UserModel] +UserModel: TypeAlias = type[_User] class RedirectURLMixin: next_page: str | None @@ -66,7 +66,7 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView): token_generator: Any validlink: bool user: Any - def get_user(self, uidb64: str) -> _UserModel | None: ... + def get_user(self, uidb64: str) -> _User | None: ... class PasswordResetCompleteView(PasswordContextMixin, TemplateView): title: Any diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index 520f0ba5f..03036714f 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -4,8 +4,7 @@ from io import BytesIO from re import Pattern from typing import Any, Awaitable, BinaryIO, Callable, Literal, NoReturn, TypeVar, overload, type_check_only -from django.contrib.auth.base_user import _UserModel -from django.contrib.auth.models import AnonymousUser +from django.contrib.auth.models import _AnyUser from django.contrib.sessions.backends.base import SessionBase from django.contrib.sites.models import Site from django.core.files import uploadedfile, uploadhandler @@ -55,9 +54,9 @@ class HttpRequest(BytesIO): # django.contrib.admin views: current_app: str # django.contrib.auth.middleware.AuthenticationMiddleware: - user: _UserModel | AnonymousUser + user: _AnyUser # django.contrib.auth.middleware.AuthenticationMiddleware: - auser: Callable[[], Awaitable[_UserModel | AnonymousUser]] + auser: Callable[[], Awaitable[_AnyUser]] # django.middleware.locale.LocaleMiddleware: LANGUAGE_CODE: str # django.contrib.sites.middleware.CurrentSiteMiddleware diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index a67b06458..dffee7919 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -8,7 +8,7 @@ from types import TracebackType from typing import Any, Generic, Literal, NoReturn, TypedDict, TypeVar, type_check_only from asgiref.typing import ASGIVersions -from django.contrib.auth.base_user import _UserModel +from django.contrib.auth.models import _User from django.contrib.sessions.backends.base import SessionBase from django.core.handlers.asgi import ASGIRequest from django.core.handlers.base import BaseHandler @@ -213,8 +213,8 @@ class ClientMixin: async def asession(self) -> SessionBase: ... def login(self, **credentials: Any) -> bool: ... async def alogin(self, **credentials: Any) -> bool: ... - def force_login(self, user: _UserModel, backend: str | None = ...) -> None: ... - async def aforce_login(self, user: _UserModel, backend: str | None = ...) -> None: ... + def force_login(self, user: _User, backend: str | None = ...) -> None: ... + async def aforce_login(self, user: _User, backend: str | None = ...) -> None: ... def logout(self) -> None: ... async def alogout(self) -> None: ... diff --git a/mypy_django_plugin/main.py b/mypy_django_plugin/main.py index 69bb54c06..befc36865 100644 --- a/mypy_django_plugin/main.py +++ b/mypy_django_plugin/main.py @@ -291,7 +291,7 @@ def get_type_analyze_hook(self, fullname: str) -> Optional[Callable[[AnalyzeType "django_stubs_ext.annotations.WithAnnotations", ): return partial(handle_annotated_type, fullname=fullname) - elif fullname == "django.contrib.auth.base_user._UserModel": + elif fullname == "django.contrib.auth.models._User": return partial(get_user_model, django_context=self.django_context) return None @@ -307,7 +307,7 @@ def get_dynamic_class_hook(self, fullname: str) -> Optional[Callable[[DynamicCla def report_config_data(self, ctx: ReportConfigContext) -> Dict[str, Any]: # Cache would be cleared if any settings do change. extra_data = {} - # In all places we use '_UserModel' alias as a type we want to clear cache if + # In all places we use '_User' alias as a type we want to clear cache if # AUTH_USER_MODEL setting changes if ctx.id.startswith("django.contrib.auth") or ctx.id in {"django.http.request", "django.test.client"}: extra_data["AUTH_USER_MODEL"] = self.django_context.settings.AUTH_USER_MODEL