Skip to content
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 TypeAliases and TypeVars for "user" and "any user" #2384

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 10 additions & 13 deletions django-stubs/contrib/auth/__init__.pyi
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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: ...
20 changes: 8 additions & 12 deletions django-stubs/contrib/auth/backends.pyi
Original file line number Diff line number Diff line change
@@ -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]: ...
Expand All @@ -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(
Expand All @@ -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): ...
6 changes: 0 additions & 6 deletions django-stubs/contrib/auth/base_user.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
5 changes: 2 additions & 3 deletions django-stubs/contrib/auth/decorators.pyi
Original file line number Diff line number Diff line change
@@ -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]: ...
Expand Down
40 changes: 20 additions & 20 deletions django-stubs/contrib/auth/forms.pyi
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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: ...

Expand All @@ -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]: ...

Expand All @@ -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 = ...,
Expand All @@ -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]: ...
7 changes: 3 additions & 4 deletions django-stubs/contrib/auth/middleware.pyi
Original file line number Diff line number Diff line change
@@ -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: ...
Expand Down
25 changes: 17 additions & 8 deletions django-stubs/contrib/auth/models.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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
# 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 = AbstractBaseUser

def update_last_login(sender: type[AbstractBaseUser], user: AbstractBaseUser, **kwargs: Any) -> None: ...
_AnyUser: TypeAlias = _User | AnonymousUser

# 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: ...
Expand All @@ -38,23 +49,21 @@ 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,
is_active: bool = ...,
include_superusers: bool = ...,
backend: str | None = ...,
obj: Model | None = ...,
) -> QuerySet[_T]: ...
) -> QuerySet[_UserType]: ...

class PermissionsMixin(models.Model):
is_superuser = models.BooleanField()
Expand Down
16 changes: 8 additions & 8 deletions django-stubs/contrib/auth/password_validation.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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]: ...

Expand All @@ -24,24 +24,24 @@ 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:
DEFAULT_USER_ATTRIBUTES: Sequence[str]
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: ...
10 changes: 5 additions & 5 deletions django-stubs/contrib/auth/tokens.pyi
Original file line number Diff line number Diff line change
@@ -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: ...

Expand Down
6 changes: 3 additions & 3 deletions django-stubs/contrib/auth/views.pyi
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading
Loading