diff --git a/README.md b/README.md index fd8fe007e..2ed7be80f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ when you will try to use `QuerySet[MyModel]`, `Manager[MyModel]` or some other D This happens because these Django classes do not support [`__class_getitem__`](https://www.python.org/dev/peps/pep-0560/#class-getitem) magic method in runtime. -1. You can go with [`django_stubs_ext`](https://github.com/typeddjango/django-stubs/tree/master/ext) helper, that patches all the types we use as Generic in django. +1. You can go with our [`django_stubs_ext`](https://github.com/typeddjango/django-stubs/tree/master/ext) helper, that patches all the types we use as Generic in django. Install it: @@ -42,7 +42,26 @@ This happens because these Django classes do not support [`__class_getitem__`](h You can add extra types to patch with `django_stubs_ext.monkeypatch(extra_classes=[YourDesiredType])` -2. You can use strings instead: `'QuerySet[MyModel]'` and `'Manager[MyModel]'`, this way it will work as a type for type-checking and as a regular `str` in runtime. + **If you use generic symbols in `django.contrib.auth.forms`**, you will have to do the monkeypatching + manually in your first [`AppConfig.ready`](https://docs.djangoproject.com/en/stable/ref/applications/#django.apps.AppConfig.ready). + This is currently required because `django.contrib.auth.forms` cannot be imported until django is initialized. + + ```python + import django_stubs_ext + from django.apps import AppConfig + + class ClientsConfig(AppConfig): + name = "clients" + + def ready(self) -> None: + from django.contrib.auth.forms import SetPasswordMixin, SetUnusablePasswordMixin + + # For Django version prior to 5.1, use `extra_classes=[SetPasswordForm, AdminPasswordChangeForm]` instead. + django_stubs_ext.monkeypatch(extra_classes=[SetPasswordMixin, SetUnusablePasswordMixin]) + ``` + + +2. You can use strings instead: `'QuerySet[MyModel]'` and `'Manager[MyModel]'`, this way it will work as a type for `mypy` and as a regular `str` in runtime. ## usage diff --git a/django-stubs/contrib/admin/decorators.pyi b/django-stubs/contrib/admin/decorators.pyi index f5f14587a..1267ddcfc 100644 --- a/django-stubs/contrib/admin/decorators.pyi +++ b/django-stubs/contrib/admin/decorators.pyi @@ -1,11 +1,12 @@ from collections.abc import Callable, Sequence -from typing import Any, TypeVar, overload +from typing import Any, overload from django.contrib.admin import ModelAdmin from django.contrib.admin.sites import AdminSite from django.db.models.base import Model from django.db.models.expressions import BaseExpression, Combinable from django.utils.functional import _StrOrPromise +from typing_extensions import TypeVar _ModelAdmin = TypeVar("_ModelAdmin", bound=ModelAdmin[Any]) _F = TypeVar("_F", bound=Callable[..., Any]) diff --git a/django-stubs/contrib/admin/forms.pyi b/django-stubs/contrib/admin/forms.pyi index fdfe7d919..32771e7f0 100644 --- a/django-stubs/contrib/admin/forms.pyi +++ b/django-stubs/contrib/admin/forms.pyi @@ -1,7 +1,7 @@ -from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm +from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm, _UserType -class AdminAuthenticationForm(AuthenticationForm): +class AdminAuthenticationForm(AuthenticationForm[_UserType]): required_css_class: str = ... -class AdminPasswordChangeForm(PasswordChangeForm): +class AdminPasswordChangeForm(PasswordChangeForm[_UserType]): required_css_class: str = ... diff --git a/django-stubs/contrib/admin/helpers.pyi b/django-stubs/contrib/admin/helpers.pyi index f0fc299d1..cfcd1e381 100644 --- a/django-stubs/contrib/admin/helpers.pyi +++ b/django-stubs/contrib/admin/helpers.pyi @@ -5,7 +5,7 @@ from django import forms from django.db.models.fields import AutoField from django.forms.boundfield import BoundField from django.forms.forms import BaseForm -from django.forms.utils import ErrorDict +from django.forms.utils import ErrorDict, ErrorList from django.forms.widgets import Media, Widget from django.utils.safestring import SafeText @@ -157,5 +157,5 @@ class InlineFieldset(Fieldset): formset: Any = ... def __init__(self, formset: Any, *args: Any, **kwargs: Any) -> None: ... -class AdminErrorList(forms.utils.ErrorList): +class AdminErrorList(ErrorList): def __init__(self, form: Any, inline_formsets: Any) -> None: ... diff --git a/django-stubs/contrib/admin/options.pyi b/django-stubs/contrib/admin/options.pyi index 70ee72e7c..dde51a0b4 100644 --- a/django-stubs/contrib/admin/options.pyi +++ b/django-stubs/contrib/admin/options.pyi @@ -1,10 +1,9 @@ import enum -from collections import OrderedDict from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence -from typing import Any, Generic, Literal, TypeAlias, TypeVar, cast +from typing import Any, ClassVar, Generic, Literal, TypeAlias, cast, type_check_only -from django.contrib.admin.filters import ListFilter -from django.contrib.admin.helpers import ActionForm +from django import forms +from django.contrib.admin.filters import FieldListFilter, ListFilter from django.contrib.admin.models import LogEntry from django.contrib.admin.sites import AdminSite from django.contrib.admin.views.main import ChangeList @@ -15,10 +14,10 @@ from django.db.models.base import Model from django.db.models.fields import Field from django.db.models.fields.related import ForeignKey, ManyToManyField, RelatedField from django.db.models.options import Options -from django.db.models.query import QuerySet +from django.db.models.query import QuerySet, _OrderByFieldName from django.forms.fields import Field as FormField from django.forms.fields import TypedChoiceField -from django.forms.forms import BaseForm +from django.forms.formsets import BaseFormSet from django.forms.models import ( BaseInlineFormSet, BaseModelFormSet, @@ -26,19 +25,21 @@ from django.forms.models import ( ModelForm, ModelMultipleChoiceField, ) -from django.forms.widgets import Media +from django.forms.widgets import Media, MediaDefiningClass from django.http.request import HttpRequest -from django.http.response import HttpResponse, HttpResponseBase, HttpResponseRedirect, JsonResponse -from django.template.response import TemplateResponse +from django.http.response import HttpResponse, HttpResponseBase, HttpResponseRedirect +from django.template.response import _TemplateForResponseT from django.urls.resolvers import URLPattern +from django.utils.datastructures import _ListOrTuple from django.utils.functional import _StrOrPromise -from django.utils.safestring import SafeText -from typing_extensions import TypedDict +from django.utils.safestring import SafeString +from typing_extensions import Self, TypedDict, TypeVar, override IS_POPUP_VAR: str TO_FIELD_VAR: str -HORIZONTAL: Literal[1] = ... -VERTICAL: Literal[2] = ... +IS_FACETS_VAR: str +HORIZONTAL: Literal[1] +VERTICAL: Literal[2] _Direction: TypeAlias = Literal[1, 2] @@ -55,168 +56,187 @@ class IncorrectLookupParameters(Exception): ... FORMFIELD_FOR_DBFIELD_DEFAULTS: Any csrf_protect_m: Any +_FieldGroups: TypeAlias = _ListOrTuple[str | _ListOrTuple[str]] + +@type_check_only class _OptionalFieldOpts(TypedDict, total=False): classes: Sequence[str] description: _StrOrPromise +@type_check_only class _FieldOpts(_OptionalFieldOpts, total=True): - fields: Sequence[str | Sequence[str]] + fields: _FieldGroups -# Workaround for mypy issue, a Sequence type should be preferred here. -# https://github.com/python/mypy/issues/8921 -# _FieldsetSpec = Sequence[tuple[str | None, _FieldOpts]] -_T = TypeVar("_T") -_ListOrTuple: TypeAlias = tuple[_T, ...] | list[_T] _FieldsetSpec: TypeAlias = _ListOrTuple[tuple[_StrOrPromise | None, _FieldOpts]] +_ListFilterT: TypeAlias = ( + type[ListFilter] + | Field[Any, Any] + | str + | tuple[Field[Any, Any] | str, type[FieldListFilter]] + | list[Field[Any, Any] | str | type[FieldListFilter]] +) # Generic type specifically for models, for use in BaseModelAdmin and subclasses # https://github.com/typeddjango/django-stubs/issues/482 _ModelT = TypeVar("_ModelT", bound=Model) +_DisplayT: TypeAlias = str | Callable[[_ModelT], str | bool] +_ListDisplayT: TypeAlias = _ListOrTuple[_DisplayT[_ModelT]] -class BaseModelAdmin(Generic[_ModelT]): - autocomplete_fields: Sequence[str] = ... - raw_id_fields: Sequence[str] = ... - fields: Sequence[str | Sequence[str]] = ... - exclude: Sequence[str] = ... - fieldsets: _FieldsetSpec = ... - form: type[BaseForm] = ... - filter_vertical: Sequence[str] = ... - filter_horizontal: Sequence[str] = ... - radio_fields: Mapping[str, _Direction] = ... - prepopulated_fields: Mapping[str, Sequence[str]] = ... - formfield_overrides: Mapping[type[Field[Any, Any]], Mapping[str, Any]] = ... - readonly_fields: Sequence[str | Callable[[_ModelT], Any]] = ... - ordering: Sequence[str] | None = ... - sortable_by: Sequence[str] | None = ... - view_on_site: bool = ... - show_full_result_count: bool = ... - checks_class: Any = ... +# Options `form`, `list_display`, `list_display_links` and `actions` are not marked as `ClassVar` due to the +# limitations of the current type system: `ClassVar` cannot contain type variables. +class BaseModelAdmin(Generic[_ModelT], metaclass=MediaDefiningClass): + autocomplete_fields: ClassVar[_ListOrTuple[str]] + raw_id_fields: ClassVar[_ListOrTuple[str]] + fields: ClassVar[_FieldGroups | None] + exclude: ClassVar[_ListOrTuple[str] | None] + fieldsets: ClassVar[_FieldsetSpec | None] + form: type[forms.ModelForm[_ModelT]] + filter_vertical: ClassVar[_ListOrTuple[str]] + filter_horizontal: ClassVar[_ListOrTuple[str]] + radio_fields: ClassVar[Mapping[str, _Direction]] + prepopulated_fields: ClassVar[dict[str, Sequence[str]]] + formfield_overrides: ClassVar[Mapping[type[Field[Any, Any]], Mapping[str, Any]]] + readonly_fields: ClassVar[_ListOrTuple[str]] + ordering: ClassVar[_ListOrTuple[_OrderByFieldName] | None] + sortable_by: ClassVar[_ListOrTuple[str] | None] + show_full_result_count: ClassVar[bool] + checks_class: ClassVar[Any] + model: type[_ModelT] + opts: Options[_ModelT] + admin_site: AdminSite + def __init__(self) -> None: ... def check(self, **kwargs: Any) -> list[CheckMessage]: ... def formfield_for_dbfield( - self, db_field: Field[Any, Any], request: HttpRequest | None, **kwargs: Any + self, db_field: Field[Any, Any], request: HttpRequest, **kwargs: Any ) -> FormField | None: ... def formfield_for_choice_field( - self, db_field: Field[Any, Any], request: HttpRequest | None, **kwargs: Any - ) -> TypedChoiceField: ... + self, db_field: Field[Any, Any], request: HttpRequest, **kwargs: Any + ) -> TypedChoiceField | None: ... def get_field_queryset( - self, db: None, db_field: RelatedField[Any, Any], request: HttpRequest | None + self, db: str | None, db_field: RelatedField[Any, Any], request: HttpRequest ) -> QuerySet[Any] | None: ... def formfield_for_foreignkey( - self, db_field: ForeignKey[Any], request: HttpRequest | None, **kwargs: Any - ) -> ModelChoiceField | None: ... + self, db_field: ForeignKey[Any], request: HttpRequest, **kwargs: Any + ) -> ModelChoiceField[Any] | None: ... def formfield_for_manytomany( - self, - db_field: ManyToManyField[Any, Any], - request: HttpRequest | None, - **kwargs: Any, - ) -> ModelMultipleChoiceField: ... - def get_autocomplete_fields(self, request: HttpRequest) -> Sequence[str]: ... + self, db_field: ManyToManyField[Any, Any], request: HttpRequest, **kwargs: Any + ) -> ModelMultipleChoiceField[Any] | None: ... + def get_autocomplete_fields(self, request: HttpRequest) -> _ListOrTuple[str]: ... def get_view_on_site_url(self, obj: _ModelT | None = ...) -> str | None: ... - def get_empty_value_display(self) -> SafeText: ... - def get_exclude(self, request: HttpRequest, obj: _ModelT | None = ...) -> Sequence[str]: ... - def get_fields(self, request: HttpRequest, obj: _ModelT | None = ...) -> Sequence[Callable[..., Any] | str]: ... - def get_fieldsets( - self, request: HttpRequest, obj: _ModelT | None = ... - ) -> list[tuple[str | None, dict[str, Any]]]: ... - def get_ordering(self, request: HttpRequest) -> list[str] | tuple[Any, ...]: ... - def get_readonly_fields(self, request: HttpRequest, obj: _ModelT | None = ...) -> list[str] | tuple[Any, ...]: ... - def get_prepopulated_fields(self, request: HttpRequest, obj: _ModelT | None = ...) -> dict[str, tuple[str]]: ... + def get_empty_value_display(self) -> SafeString: ... + def get_exclude(self, request: HttpRequest, obj: _ModelT | None = ...) -> _ListOrTuple[str] | None: ... + def get_fields(self, request: HttpRequest, obj: _ModelT | None = ...) -> _FieldGroups: ... + def get_fieldsets(self, request: HttpRequest, obj: _ModelT | None = ...) -> _FieldsetSpec: ... + def get_inlines( + self, request: HttpRequest, obj: _ModelT | None + ) -> _ListOrTuple[type[InlineModelAdmin[Any, Any]]]: ... + def get_ordering(self, request: HttpRequest) -> _ListOrTuple[_OrderByFieldName]: ... + def get_readonly_fields(self, request: HttpRequest, obj: _ModelT | None = ...) -> _ListOrTuple[str]: ... + def get_prepopulated_fields(self, request: HttpRequest, obj: _ModelT | None = ...) -> dict[str, Sequence[str]]: ... def get_queryset(self, request: HttpRequest) -> QuerySet[_ModelT]: ... - def get_sortable_by(self, request: HttpRequest) -> list[Callable[..., Any]] | list[str] | tuple[Any, ...]: ... - def get_inlines(self, request: HttpRequest, obj: _ModelT | None) -> Iterable[type[InlineModelAdmin[Any]]]: ... - def lookup_allowed(self, lookup: str, value: str) -> bool: ... + def get_sortable_by(self, request: HttpRequest) -> _ListDisplayT[_ModelT]: ... + def lookup_allowed(self, lookup: str, value: str, request: HttpRequest) -> bool: ... def to_field_allowed(self, request: HttpRequest, to_field: str) -> bool: ... def has_add_permission(self, request: HttpRequest) -> bool: ... def has_change_permission(self, request: HttpRequest, obj: _ModelT | None = ...) -> bool: ... def has_delete_permission(self, request: HttpRequest, obj: _ModelT | None = ...) -> bool: ... def has_view_permission(self, request: HttpRequest, obj: _ModelT | None = ...) -> bool: ... + def has_view_or_change_permission(self, request: HttpRequest, obj: _ModelT | None = ...) -> bool: ... def has_module_permission(self, request: HttpRequest) -> bool: ... + view_on_site: Callable[[_ModelT], str | None] | bool + @property + def media(self) -> Media: ... + +_ModelAdmin = TypeVar("_ModelAdmin", bound=ModelAdmin[Any]) +_ActionCallable: TypeAlias = Callable[[_ModelAdmin, HttpRequest, QuerySet[_ModelT]], HttpResponseBase | None] class ModelAdmin(BaseModelAdmin[_ModelT]): - list_display: Sequence[str | Callable[[_ModelT], Any]] = ... - list_display_links: Sequence[str | Callable[..., Any]] | None = ... - list_filter: Sequence[str | type[ListFilter] | tuple[str, type[ListFilter]]] = ... - list_select_related: bool | Sequence[str] = ... - list_per_page: int = ... - list_max_show_all: int = ... - list_editable: Sequence[str] = ... - search_fields: Sequence[str] = ... - search_help_text: _StrOrPromise | None = ... - date_hierarchy: str | None = ... - save_as: bool = ... - save_as_continue: bool = ... - save_on_top: bool = ... - paginator: type[Any] = ... - preserve_filters: bool = ... - inlines: Sequence[type[InlineModelAdmin[Any]]] = ... - add_form_template: str = ... - change_form_template: str = ... - change_list_template: str = ... - delete_confirmation_template: str = ... - delete_selected_confirmation_template: str = ... - object_history_template: str = ... - popup_response_template: str = ... - actions: Sequence[Callable[[ModelAdmin[Any], HttpRequest, QuerySet[Any]], HttpResponse | None] | str] | None = ... - action_form: type[ActionForm] = ... - actions_on_top: bool = ... - actions_on_bottom: bool = ... - actions_selection_counter: bool = ... - model: type[_ModelT] = ... - opts: Options[_ModelT] = ... - admin_site: AdminSite = ... - def __init__(self, model: type[_ModelT], admin_site: AdminSite | None) -> None: ... - def get_inline_instances(self, request: HttpRequest, obj: _ModelT | None = ...) -> list[InlineModelAdmin[Any]]: ... + list_display: _ListDisplayT[_ModelT] + list_display_links: _ListDisplayT[_ModelT] | None + list_filter: ClassVar[_ListOrTuple[_ListFilterT]] + list_select_related: ClassVar[bool | _ListOrTuple[str]] + list_per_page: ClassVar[int] + list_max_show_all: ClassVar[int] + list_editable: ClassVar[_ListOrTuple[str]] + search_fields: ClassVar[_ListOrTuple[str]] + search_help_text: ClassVar[_StrOrPromise | None] + date_hierarchy: ClassVar[str | None] + save_as: ClassVar[bool] + save_as_continue: ClassVar[bool] + save_on_top: ClassVar[bool] + paginator: ClassVar[type] + preserve_filters: ClassVar[bool] + show_facets: ClassVar[ShowFacets] + inlines: ClassVar[_ListOrTuple[type[InlineModelAdmin[Any, Any]]]] + add_form_template: ClassVar[_TemplateForResponseT | None] + change_form_template: ClassVar[_TemplateForResponseT | None] + change_list_template: ClassVar[_TemplateForResponseT | None] + delete_confirmation_template: ClassVar[_TemplateForResponseT | None] + delete_selected_confirmation_template: ClassVar[_TemplateForResponseT | None] + object_history_template: ClassVar[_TemplateForResponseT | None] + popup_response_template: ClassVar[_TemplateForResponseT | None] + actions: Sequence[_ActionCallable[Self, _ModelT] | str] | None + action_form: ClassVar[Any] + actions_on_top: ClassVar[bool] + actions_on_bottom: ClassVar[bool] + actions_selection_counter: ClassVar[bool] + model: type[_ModelT] + opts: Options[_ModelT] + admin_site: AdminSite + def __init__(self, model: type[_ModelT], admin_site: AdminSite) -> None: ... + def get_inline_instances( + self, request: HttpRequest, obj: _ModelT | None = ... + ) -> list[InlineModelAdmin[Any, Any]]: ... def get_urls(self) -> list[URLPattern]: ... @property def urls(self) -> list[URLPattern]: ... @property + @override def media(self) -> Media: ... def get_model_perms(self, request: HttpRequest) -> dict[str, bool]: ... def get_form( - self, - request: HttpRequest, - obj: _ModelT | None = ..., - change: bool = ..., - **kwargs: Any, - ) -> type[ModelForm]: ... + self, request: HttpRequest, obj: _ModelT | None = ..., change: bool = ..., **kwargs: Any + ) -> type[forms.ModelForm[_ModelT]]: ... def get_changelist(self, request: HttpRequest, **kwargs: Any) -> type[ChangeList]: ... def get_changelist_instance(self, request: HttpRequest) -> ChangeList: ... - def get_object(self, request: HttpRequest, object_id: str, from_field: None = ...) -> _ModelT | None: ... - def get_changelist_form(self, request: HttpRequest, **kwargs: Any) -> type[ModelForm]: ... - def get_changelist_formset(self, request: HttpRequest, **kwargs: Any) -> type[BaseModelFormSet]: ... + def get_object(self, request: HttpRequest, object_id: str, from_field: str | None = ...) -> _ModelT | None: ... + def get_changelist_form(self, request: HttpRequest, **kwargs: Any) -> type[ModelForm[_ModelT]]: ... + def get_changelist_formset( + self, request: HttpRequest, **kwargs: Any + ) -> type[BaseModelFormSet[_ModelT, ModelForm[_ModelT]]]: ... def get_formsets_with_inlines(self, request: HttpRequest, obj: _ModelT | None = ...) -> Iterator[Any]: ... def get_paginator( self, request: HttpRequest, - queryset: QuerySet[_ModelT], + queryset: QuerySet, per_page: int, orphans: int = ..., allow_empty_first_page: bool = ..., ) -> Paginator: ... - def log_addition(self, request: HttpRequest, object: _ModelT, message: Any) -> LogEntry: ... - def log_change(self, request: HttpRequest, object: _ModelT, message: Any) -> LogEntry: ... - def log_deletion(self, request: HttpRequest, object: _ModelT, object_repr: str) -> LogEntry: ... - def action_checkbox(self, obj: _ModelT) -> SafeText: ... - def get_actions(self, request: HttpRequest) -> OrderedDict[Any, Any]: ... + def log_addition(self, request: HttpRequest, obj: _ModelT, message: Any) -> LogEntry: ... + def log_change(self, request: HttpRequest, obj: _ModelT, message: Any) -> LogEntry: ... + def log_deletions(self, request: HttpRequest, queryset: QuerySet[_ModelT]) -> list[LogEntry]: ... + def action_checkbox(self, obj: _ModelT) -> SafeString: ... + def get_actions(self, request: HttpRequest) -> dict[str, tuple[Callable[..., str], str, str] | None]: ... def get_action_choices( self, request: HttpRequest, default_choices: list[tuple[str, str]] = ... ) -> list[tuple[str, str]]: ... - def get_action(self, action: Callable[..., Any] | str) -> tuple[Callable[..., Any], str, str]: ... - def get_list_display(self, request: HttpRequest) -> Sequence[str]: ... - def get_list_display_links(self, request: HttpRequest, list_display: Sequence[str]) -> Sequence[str] | None: ... - def get_list_filter(self, request: HttpRequest) -> Sequence[str]: ... - def get_list_select_related(self, request: HttpRequest) -> Sequence[str]: ... - def get_search_fields(self, request: HttpRequest) -> list[str]: ... + def get_action(self, action: Callable | str) -> tuple[Callable[..., str], str, str] | None: ... + def get_list_display(self, request: HttpRequest) -> _ListDisplayT[_ModelT]: ... + def get_list_display_links( + self, request: HttpRequest, list_display: _ListDisplayT[_ModelT] + ) -> _ListDisplayT[_ModelT]: ... + def get_list_filter(self, request: HttpRequest) -> _ListOrTuple[_ListFilterT]: ... + def get_list_select_related(self, request: HttpRequest) -> bool | _ListOrTuple[str]: ... + def get_search_fields(self, request: HttpRequest) -> _ListOrTuple[str]: ... def get_search_results( self, request: HttpRequest, queryset: QuerySet[Any], search_term: str - ) -> tuple[QuerySet[Any], bool]: ... + ) -> tuple[QuerySet[_ModelT], bool]: ... def get_preserved_filters(self, request: HttpRequest) -> str: ... + def _get_edited_object_pks(self, request: HttpRequest, prefix: str) -> list[str]: ... + def _get_list_editable_queryset(self, request: HttpRequest, prefix: str) -> QuerySet[_ModelT]: ... def construct_change_message( - self, - request: HttpRequest, - form: BaseForm, - formsets: None, - add: bool = ..., + self, request: HttpRequest, form: forms.Form, formsets: Iterable[BaseFormSet], add: bool = ... ) -> list[dict[str, dict[str, list[str]]]]: ... def message_user( self, @@ -226,114 +246,125 @@ class ModelAdmin(BaseModelAdmin[_ModelT]): extra_tags: str = ..., fail_silently: bool = ..., ) -> None: ... - def save_form(self, request: HttpRequest, form: ModelForm, change: bool) -> _ModelT: ... - def save_model(self, request: HttpRequest, obj: _ModelT, form: ModelForm, change: bool) -> None: ... + def save_form(self, request: HttpRequest, form: Any, change: Any) -> Any: ... + def save_model(self, request: HttpRequest, obj: _ModelT, form: Any, change: Any) -> None: ... def delete_model(self, request: HttpRequest, obj: _ModelT) -> None: ... - def delete_queryset(self, request: HttpRequest, queryset: QuerySet[_ModelT]) -> None: ... - def save_formset( - self, - request: HttpRequest, - form: ModelForm, - formset: BaseModelFormSet, - change: bool, - ) -> None: ... - def save_related( - self, - request: HttpRequest, - form: ModelForm, - formsets: BaseModelFormSet, - change: bool, - ) -> None: ... + def delete_queryset(self, request: HttpRequest, queryset: QuerySet) -> None: ... + def save_formset(self, request: HttpRequest, form: Any, formset: Any, change: Any) -> None: ... + def save_related(self, request: HttpRequest, form: Any, formsets: Any, change: Any) -> None: ... def render_change_form( self, request: HttpRequest, - context: Mapping[str, Any], + context: dict[str, Any], add: bool = ..., change: bool = ..., form_url: str = ..., obj: _ModelT | None = ..., - ) -> TemplateResponse: ... + ) -> HttpResponse: ... def response_add(self, request: HttpRequest, obj: _ModelT, post_url_continue: str | None = ...) -> HttpResponse: ... def response_change(self, request: HttpRequest, obj: _ModelT) -> HttpResponse: ... def response_post_save_add(self, request: HttpRequest, obj: _ModelT) -> HttpResponseRedirect: ... def response_post_save_change(self, request: HttpRequest, obj: _ModelT) -> HttpResponseRedirect: ... - def response_action(self, request: HttpRequest, queryset: QuerySet[_ModelT]) -> HttpResponseBase | None: ... + # Probably FileResponse cannot come from ModelAdmin views + def response_action(self, request: HttpRequest, queryset: QuerySet) -> HttpResponse | None: ... def response_delete(self, request: HttpRequest, obj_display: str, obj_id: int) -> HttpResponse: ... - def render_delete_form(self, request: HttpRequest, context: Mapping[str, Any]) -> TemplateResponse: ... + def render_delete_form(self, request: HttpRequest, context: dict[str, Any]) -> HttpResponse: ... def get_inline_formsets( - self, - request: HttpRequest, - formsets: list[Any], - inline_instances: list[Any], - obj: _ModelT | None = ..., + self, request: HttpRequest, formsets: list[Any], inline_instances: list[Any], obj: _ModelT | None = ... ) -> list[Any]: ... - def get_changeform_initial_data(self, request: HttpRequest) -> dict[str, str]: ... + def get_changeform_initial_data(self, request: HttpRequest) -> dict[str, str | list[str]]: ... def changeform_view( self, request: HttpRequest, object_id: str | None = ..., form_url: str = ..., - extra_context: dict[str, bool] | None = ..., - ) -> TemplateResponse: ... - def autocomplete_view(self, request: HttpRequest) -> JsonResponse: ... - def add_view(self, request: HttpRequest, form_url: str = ..., extra_context: None = ...) -> HttpResponse: ... + extra_context: dict[str, Any] | None = ..., + ) -> HttpResponse: ... + def add_view( + self, request: HttpRequest, form_url: str = ..., extra_context: dict[str, Any] | None = ... + ) -> HttpResponse: ... def change_view( - self, - request: HttpRequest, - object_id: str, - form_url: str = ..., - extra_context: dict[str, bool] | None = ..., + self, request: HttpRequest, object_id: str, form_url: str = ..., extra_context: dict[str, Any] | None = ... ) -> HttpResponse: ... - def changelist_view(self, request: HttpRequest, extra_context: dict[str, str] | None = ...) -> TemplateResponse: ... + def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = ...) -> HttpResponse: ... def get_deleted_objects( - self, objs: QuerySet[Any], request: HttpRequest - ) -> tuple[list[Any], dict[Any, Any], set[Any], list[Any]]: ... + self, objs: Sequence[_ModelT] | QuerySet[_ModelT], request: HttpRequest + ) -> tuple[list[str], dict[str, int], set[str], list[str]]: ... def delete_view( - self, - request: HttpRequest, - object_id: str, - extra_context: dict[str, str] | None = ..., + self, request: HttpRequest, object_id: str, extra_context: dict[str, Any] | None = ... ) -> HttpResponse: ... def history_view( - self, - request: HttpRequest, - object_id: str, - extra_context: dict[str, str] | None = ..., + self, request: HttpRequest, object_id: str, extra_context: dict[str, Any] | None = ... ) -> HttpResponse: ... def get_formset_kwargs( - self, - request: HttpRequest, - obj: _ModelT, - inline: InlineModelAdmin[Any], - prefix: str, + self, request: HttpRequest, obj: _ModelT, inline: InlineModelAdmin[Any, _ModelT], prefix: str ) -> dict[str, Any]: ... -class InlineModelAdmin(BaseModelAdmin[_ModelT]): - model: type[_ModelT] = ... - fk_name: str = ... - formset: type[BaseInlineFormSet] = ... - extra: int = ... - min_num: int | None = ... - max_num: int | None = ... - template: str = ... - verbose_name: _StrOrPromise | None = ... - verbose_name_plural: _StrOrPromise | None = ... - can_delete: bool = ... - show_change_link: bool = ... - classes: Sequence[str] | None = ... - admin_site: AdminSite = ... - parent_model: Any = ... - opts: Any = ... - has_registered_model: Any = ... - def __init__(self, parent_model: type[_ModelT] | _ModelT, admin_site: AdminSite) -> None: ... +_ChildModelT = TypeVar("_ChildModelT", bound=Model) +_ParentModelT = TypeVar("_ParentModelT", bound=Model) + +class InlineModelAdmin(BaseModelAdmin[Any], Generic[_ChildModelT, _ParentModelT]): + model: type[_ChildModelT] # pyright: ignore[reportIncompatibleVariableOverride] + form: type[forms.ModelForm[_ChildModelT]] + fk_name: str | None + formset: type[BaseInlineFormSet[_ChildModelT, _ParentModelT, forms.ModelForm[_ChildModelT]]] + extra: int + min_num: int | None + max_num: int | None + template: str + verbose_name: _StrOrPromise | None + verbose_name_plural: _StrOrPromise | None + can_delete: bool + show_change_link: bool + classes: Sequence[str] | None + admin_site: AdminSite + parent_model: type[_ParentModelT] + opts: Options[_ChildModelT] + has_registered_model: bool + def __init__(self, parent_model: type[_ParentModelT], admin_site: AdminSite) -> None: ... @property + @override def media(self) -> Media: ... - def get_extra(self, request: HttpRequest, obj: _ModelT | None = ..., **kwargs: Any) -> int: ... - def get_min_num(self, request: HttpRequest, obj: _ModelT | None = ..., **kwargs: Any) -> int | None: ... - def get_max_num(self, request: HttpRequest, obj: _ModelT | None = ..., **kwargs: Any) -> int | None: ... + def get_extra(self, request: HttpRequest, obj: _ParentModelT | None = ..., **kwargs: Any) -> int: ... + def get_min_num(self, request: HttpRequest, obj: _ParentModelT | None = ..., **kwargs: Any) -> int | None: ... + def get_max_num(self, request: HttpRequest, obj: _ParentModelT | None = ..., **kwargs: Any) -> int | None: ... def get_formset( - self, request: HttpRequest, obj: _ModelT | None = ..., **kwargs: Any - ) -> type[BaseInlineFormSet]: ... + self, request: HttpRequest, obj: _ParentModelT | None = ..., **kwargs: Any + ) -> type[BaseInlineFormSet[_ChildModelT, _ParentModelT, forms.ModelForm[_ChildModelT]]]: ... + @override + def get_exclude(self, request: HttpRequest, obj: _ParentModelT | None = ...) -> _ListOrTuple[str] | None: ... + @override + def get_fields(self, request: HttpRequest, obj: _ParentModelT | None = ...) -> _FieldGroups: ... + @override + def get_fieldsets(self, request: HttpRequest, obj: _ParentModelT | None = ...) -> _FieldsetSpec: ... + @override + def get_inlines( + self, request: HttpRequest, obj: _ParentModelT | None + ) -> _ListOrTuple[type[InlineModelAdmin[Any, Any]]]: ... + @override + def get_readonly_fields(self, request: HttpRequest, obj: _ParentModelT | None = ...) -> _ListOrTuple[str]: ... + @override + def get_prepopulated_fields( + self, request: HttpRequest, obj: _ParentModelT | None = ... + ) -> dict[str, Sequence[str]]: ... + @override + def has_view_or_change_permission(self, request: HttpRequest, obj: _ParentModelT | None = ...) -> bool: ... + @override + def get_view_on_site_url(self, obj: _ChildModelT | None = ...) -> str | None: ... + view_on_site: Callable[[_ChildModelT], str | None] | bool + @override + def get_queryset(self, request: HttpRequest) -> QuerySet[_ChildModelT]: ... + @override + def has_add_permission(self, request: HttpRequest, obj: _ParentModelT | None) -> bool: ... # type: ignore[override] + @override + def has_change_permission(self, request: HttpRequest, obj: _ParentModelT | None = ...) -> bool: ... + @override + def has_delete_permission(self, request: HttpRequest, obj: _ParentModelT | None = ...) -> bool: ... + @override + def has_view_permission(self, request: HttpRequest, obj: _ParentModelT | None = ...) -> bool: ... + +class StackedInline(InlineModelAdmin[_ChildModelT, _ParentModelT]): + template: str -class StackedInline(InlineModelAdmin[_ModelT]): ... -class TabularInline(InlineModelAdmin[_ModelT]): ... +class TabularInline(InlineModelAdmin[_ChildModelT, _ParentModelT]): + template: str diff --git a/django-stubs/contrib/auth/forms.pyi b/django-stubs/contrib/auth/forms.pyi index f51226f41..928a8a007 100644 --- a/django-stubs/contrib/auth/forms.pyi +++ b/django-stubs/contrib/auth/forms.pyi @@ -1,49 +1,113 @@ -from collections.abc import Iterator -from typing import Any +from collections.abc import Iterable +from logging import Logger +from typing import Any, ClassVar, Generic, TypeVar from django import forms from django.contrib.auth.base_user import AbstractBaseUser -from django.contrib.auth.models import User from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.core.exceptions import ValidationError -from django.core.handlers.wsgi import WSGIRequest +from django.db import models +from django.db.models.fields import _ErrorMessagesDict +from django.forms.widgets import Widget +from django.http.request import HttpRequest +from django.utils.functional import _StrOrPromise, cached_property +from typing_extensions import override +logger: Logger UserModel: Any +_UserType = TypeVar("_UserType", bound=AbstractBaseUser) + class ReadOnlyPasswordHashWidget(forms.Widget): - template_name: str = ... + template_name: str + @override + def get_context(self, name: str, value: Any, attrs: dict[str, Any] | None) -> dict[str, Any]: ... class ReadOnlyPasswordHashField(forms.Field): + widget: Any def __init__(self, *args: Any, **kwargs: Any) -> None: ... -class UsernameField(forms.CharField): ... +class UsernameField(forms.CharField): + @override + def to_python(self, value: Any | None) -> Any | None: ... + @override + def widget_attrs(self, widget: Widget) -> dict[str, Any]: ... + +class SetPasswordMixin(Generic[_UserType]): + error_messages: _ErrorMessagesDict + + @staticmethod + def create_password_fields( + label1: _StrOrPromise = ..., label2: _StrOrPromise = ... + ) -> tuple[forms.CharField, forms.CharField]: ... + def validate_passwords( + self, + password1_field_name: str = ..., + password2_field_name: str = ..., + ) -> None: ... + def validate_password_for_user(self, user: _UserType, password_field_name: str = "password2") -> None: ... + def set_password_and_save( + self, user: _UserType, password_field_name: str = "password1", commit: bool = True + ) -> _UserType: ... + def __class_getitem__(cls, *args: Any, **kwargs: Any) -> Any: ... + +class SetUnusablePasswordMixin(Generic[_UserType]): + usable_password_help_text: _StrOrPromise + + @staticmethod + def create_usable_password_field(help_text: _StrOrPromise = ...) -> forms.ChoiceField: ... + def validate_passwords( + self, + password1_field_name: str = ..., + password2_field_name: str = ..., + usable_password_field_name: str = ..., + ) -> None: ... + def validate_password_for_user(self, user: _UserType, **kwargs: Any) -> None: ... + def set_password_and_save(self, user: _UserType, commit: bool = True, **kwargs: Any) -> _UserType: ... + +class BaseUserCreationForm(forms.ModelForm, Generic[_UserType]): + error_messages: _ErrorMessagesDict + password1: forms.Field + password2: forms.Field + + class Meta: + model: ClassVar[type[Any]] + fields: ClassVar[tuple[str, ...]] + field_classes: ClassVar[dict[str, type[forms.Field]]] -class UserCreationForm(forms.ModelForm): - error_messages: Any = ... - password1: Any = ... - password2: Any = ... def __init__(self, *args: Any, **kwargs: Any) -> None: ... - def clean_password2(self) -> str: ... + @override + def save(self, commit: bool = ...) -> _UserType: ... + +class UserCreationForm(BaseUserCreationForm[_UserType]): + def clean_username(self) -> str: ... class UserChangeForm(forms.ModelForm): - password: Any = ... + password: forms.Field + + class Meta: + model: ClassVar[type[Any]] + fields: ClassVar[str] + field_classes: ClassVar[dict[str, type[forms.Field]]] + def __init__(self, *args: Any, **kwargs: Any) -> None: ... - def clean_password(self) -> str: ... - -class AuthenticationForm(forms.Form): - username: Any = ... - password: Any = ... - error_messages: Any = ... - request: WSGIRequest = ... - user_cache: AbstractBaseUser | None = ... - username_field: Any = ... - def __init__(self, request: Any = ..., *args: Any, **kwargs: Any) -> None: ... - def confirm_login_allowed(self, user: AbstractBaseUser) -> None: ... - def get_user(self) -> User: ... + +class AuthenticationForm(forms.Form, Generic[_UserType]): + username: forms.Field + password: forms.Field + error_messages: _ErrorMessagesDict + request: HttpRequest | None + user_cache: _UserType | None + username_field: models.Field[Any, Any] + def __init__(self, request: HttpRequest | None = ..., *args: Any, **kwargs: Any) -> None: ... + def confirm_login_allowed(self, user: _UserType) -> None: ... + def get_user(self) -> _UserType: ... def get_invalid_login_error(self) -> ValidationError: ... + @override + def clean(self) -> dict[str, Any]: ... -class PasswordResetForm(forms.Form): - email: Any = ... +class PasswordResetForm(forms.Form, Generic[_UserType]): + email: forms.Field def send_mail( self, subject_template_name: str, @@ -53,7 +117,7 @@ class PasswordResetForm(forms.Form): to_email: str, html_email_template_name: str | None = ..., ) -> None: ... - def get_users(self, email: str) -> Iterator[Any]: ... + def get_users(self, email: str) -> Iterable[_UserType]: ... def save( self, domain_override: str | None = ..., @@ -62,30 +126,34 @@ class PasswordResetForm(forms.Form): use_https: bool = ..., token_generator: PasswordResetTokenGenerator = ..., from_email: str | None = ..., - request: WSGIRequest | None = ..., + request: HttpRequest | None = ..., html_email_template_name: str | None = ..., extra_email_context: dict[str, str] | None = ..., ) -> None: ... -class SetPasswordForm(forms.Form): - error_messages: Any = ... - new_password1: Any = ... - new_password2: Any = ... - user: User = ... - def __init__(self, user: AbstractBaseUser | None, *args: Any, **kwargs: Any) -> None: ... - def clean_new_password2(self) -> str: ... - def save(self, commit: bool = ...) -> AbstractBaseUser: ... - -class PasswordChangeForm(SetPasswordForm): - old_password: Any = ... +class SetPasswordForm(SetPasswordMixin[_UserType], forms.Form, Generic[_UserType]): + new_password1: forms.Field + new_password2: forms.Field + user: _UserType + def __init__(self, user: _UserType, *args: Any, **kwargs: Any) -> None: ... + def save(self, commit: bool = ...) -> _UserType: ... + +class PasswordChangeForm(SetPasswordForm[_UserType]): + old_password: forms.Field def clean_old_password(self) -> str: ... -class AdminPasswordChangeForm(forms.Form): - error_messages: Any = ... - required_css_class: str = ... - password1: Any = ... - password2: Any = ... - user: User = ... - def __init__(self, user: AbstractBaseUser, *args: Any, **kwargs: Any) -> None: ... - def clean_password2(self) -> str: ... - def save(self, commit: bool = ...) -> AbstractBaseUser: ... +class AdminPasswordChangeForm(forms.Form, Generic[_UserType]): + error_messages: _ErrorMessagesDict + required_css_class: str + usable_password_help_text: str + password1: forms.Field + password2: forms.Field + user: _UserType + def __init__(self, user: _UserType, *args: Any, **kwargs: Any) -> None: ... + def save(self, commit: bool = ...) -> _UserType: ... + @cached_property + @override + def changed_data(self) -> list[str]: ... + +class AdminUserCreationForm(SetUnusablePasswordMixin[_UserType], UserCreationForm[_UserType]): + usable_password: forms.ChoiceField = ... diff --git a/django-stubs/contrib/auth/views.pyi b/django-stubs/contrib/auth/views.pyi index b68770448..343f3eaff 100644 --- a/django-stubs/contrib/auth/views.pyi +++ b/django-stubs/contrib/auth/views.pyi @@ -1,7 +1,7 @@ from typing import Any from django.contrib.auth.base_user import AbstractBaseUser -from django.contrib.auth.forms import AuthenticationForm +from django.contrib.auth.forms import AuthenticationForm, _UserType from django.http.request import HttpRequest from django.http.response import HttpResponseRedirect from django.template.response import TemplateResponse @@ -15,7 +15,7 @@ class SuccessURLAllowedHostsMixin: success_url_allowed_hosts: Any = ... def get_success_url_allowed_hosts(self) -> set[str]: ... -class LoginView(SuccessURLAllowedHostsMixin, FormView[AuthenticationForm]): +class LoginView(SuccessURLAllowedHostsMixin, FormView[AuthenticationForm[_UserType]]): authentication_form: Any = ... redirect_field_name: Any = ... redirect_authenticated_user: bool = ... diff --git a/django-stubs/contrib/contenttypes/admin.pyi b/django-stubs/contrib/contenttypes/admin.pyi index dbd9f2d21..0c5f68027 100644 --- a/django-stubs/contrib/contenttypes/admin.pyi +++ b/django-stubs/contrib/contenttypes/admin.pyi @@ -1,14 +1,21 @@ from typing import Any +from django.contrib.admin.checks import InlineModelAdminChecks from django.contrib.admin.options import InlineModelAdmin +from django.contrib.contenttypes.forms import BaseGenericInlineFormSet from django.db.models.base import Model -class GenericInlineModelAdminChecks: +class GenericInlineModelAdminChecks(InlineModelAdminChecks): def _check_exclude_of_parent_model(self, obj: GenericInlineModelAdmin, parent_model: type[Model]) -> list[Any]: ... def _check_relation(self, obj: GenericInlineModelAdmin, parent_model: type[Model]) -> list[Any]: ... -class GenericInlineModelAdmin(InlineModelAdmin[Any]): - template: str = ... +class GenericInlineModelAdmin(InlineModelAdmin): + ct_field: str + ct_fk_field: str + formset: type[BaseGenericInlineFormSet] # type: ignore[assignment] -class GenericStackedInline(GenericInlineModelAdmin): ... -class GenericTabularInline(GenericInlineModelAdmin): ... +class GenericStackedInline(GenericInlineModelAdmin): + template: str + +class GenericTabularInline(GenericInlineModelAdmin): + template: str diff --git a/django-stubs/contrib/flatpages/admin.pyi b/django-stubs/contrib/flatpages/admin.pyi index dc78a95ed..068100f6c 100644 --- a/django-stubs/contrib/flatpages/admin.pyi +++ b/django-stubs/contrib/flatpages/admin.pyi @@ -1,10 +1,11 @@ -from typing import Any +from typing import Any, ClassVar -import django.contrib.admin as admin +from django.contrib import admin +from django.contrib.flatpages.models import FlatPage -class FlatPageAdmin(admin.ModelAdmin[Any]): - form: Any = ... - fieldsets: Any = ... - list_display: Any = ... - list_filter: Any = ... - search_fields: Any = ... +class FlatPageAdmin(admin.ModelAdmin[FlatPage]): + form: Any + fieldsets: ClassVar[Any] + list_display: Any + list_filter: ClassVar[Any] + search_fields: ClassVar[Any] diff --git a/django-stubs/contrib/flatpages/sitemaps.pyi b/django-stubs/contrib/flatpages/sitemaps.pyi index ecfb31f53..e67408371 100644 --- a/django-stubs/contrib/flatpages/sitemaps.pyi +++ b/django-stubs/contrib/flatpages/sitemaps.pyi @@ -1,5 +1,3 @@ -from typing import Any - from django.contrib.sitemaps import Sitemap -class FlatPageSitemap(Sitemap[Any]): ... +class FlatPageSitemap(Sitemap): ... diff --git a/django-stubs/contrib/gis/db/models/fields.pyi b/django-stubs/contrib/gis/db/models/fields.pyi index 1a95ab186..9953ac5f2 100644 --- a/django-stubs/contrib/gis/db/models/fields.pyi +++ b/django-stubs/contrib/gis/db/models/fields.pyi @@ -1,7 +1,8 @@ from collections.abc import Iterable from typing import Any, NamedTuple, TypeVar -from django.db.models.fields import Field, _ErrorMessagesToOverride, _FieldChoices, _ValidatorCallable +from django.core.validators import _ValidatorCallable +from django.db.models.fields import Field, _ErrorMessagesMapping, _FieldChoices from django.utils.functional import _StrOrPromise # __set__ value type @@ -42,7 +43,7 @@ class BaseSpatialField(Field[_ST, _GT]): db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> None: ... def deconstruct(self) -> Any: ... def db_type(self, connection: Any) -> Any: ... @@ -91,7 +92,7 @@ class GeometryField(BaseSpatialField[Any, Any]): db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> None: ... def deconstruct(self) -> Any: ... def formfield(self, **kwargs: Any) -> Any: ... diff --git a/django-stubs/contrib/gis/forms/fields.pyi b/django-stubs/contrib/gis/forms/fields.pyi index d6860e880..89712cbc4 100644 --- a/django-stubs/contrib/gis/forms/fields.pyi +++ b/django-stubs/contrib/gis/forms/fields.pyi @@ -5,7 +5,6 @@ import django.forms as forms class GeometryField(forms.Field): widget: Any = ... geom_type: str = ... - default_error_messages: Any = ... srid: Any = ... def __init__(self, *, srid: Any | None = ..., geom_type: Any | None = ..., **kwargs: Any) -> None: ... def to_python(self, value: Any) -> Any: ... diff --git a/django-stubs/contrib/gis/sitemaps/kml.pyi b/django-stubs/contrib/gis/sitemaps/kml.pyi index 089eaa1bc..e5e34970f 100644 --- a/django-stubs/contrib/gis/sitemaps/kml.pyi +++ b/django-stubs/contrib/gis/sitemaps/kml.pyi @@ -3,7 +3,7 @@ from typing import Any from django.contrib.sitemaps import Sitemap from typing_extensions import override -class KMLSitemap(Sitemap[Any]): +class KMLSitemap(Sitemap): geo_format: str locations: Any def __init__(self, locations: Any | None = ...) -> None: ... diff --git a/django-stubs/contrib/postgres/fields/array.pyi b/django-stubs/contrib/postgres/fields/array.pyi index f59298398..cb8abebe8 100644 --- a/django-stubs/contrib/postgres/fields/array.pyi +++ b/django-stubs/contrib/postgres/fields/array.pyi @@ -1,8 +1,9 @@ from collections.abc import Callable, Iterable from typing import Any, Generic, Literal, TypeVar, overload +from django.core.validators import _ValidatorCallable from django.db.models.expressions import Combinable -from django.db.models.fields import Field, _ErrorMessagesToOverride, _ValidatorCallable +from django.db.models.fields import Field, _ErrorMessagesMapping from django.utils.functional import _StrOrPromise from .mixins import CheckFieldDefaultMixin @@ -11,7 +12,6 @@ _V = TypeVar("_V", bound=Any | None) class ArrayField(CheckFieldDefaultMixin, Field[_V | Combinable, _V], Generic[_V]): empty_strings_allowed: bool = ... - default_error_messages: Any = ... size: int | None = ... default_validators: Any = ... from_db_value: Any = ... @@ -42,7 +42,7 @@ class ArrayField(CheckFieldDefaultMixin, Field[_V | Combinable, _V], Generic[_V] db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> ArrayField[list[_V]]: ... @overload def __new__( @@ -70,7 +70,7 @@ class ArrayField(CheckFieldDefaultMixin, Field[_V | Combinable, _V], Generic[_V] db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> ArrayField[list[_V] | None]: ... @property def description(self) -> str: ... # type: ignore [override] diff --git a/django-stubs/contrib/postgres/fields/jsonb.pyi b/django-stubs/contrib/postgres/fields/jsonb.pyi index 0dd222f28..9e7eba076 100644 --- a/django-stubs/contrib/postgres/fields/jsonb.pyi +++ b/django-stubs/contrib/postgres/fields/jsonb.pyi @@ -15,7 +15,6 @@ class JsonAdapter: class JSONField(CheckFieldDefaultMixin, Field[Any, Any]): empty_strings_allowed: bool = ... description: Any = ... - default_error_messages: Any = ... encoder: Any = ... def __init__( self, diff --git a/django-stubs/contrib/postgres/search.pyi b/django-stubs/contrib/postgres/search.pyi index 8a8981218..cfa1fd024 100644 --- a/django-stubs/contrib/postgres/search.pyi +++ b/django-stubs/contrib/postgres/search.pyi @@ -1,9 +1,10 @@ from collections.abc import Iterable from typing import Any, TypeAlias +from django.core.validators import _ValidatorCallable from django.db.models import Field from django.db.models.expressions import Combinable, CombinedExpression, Func, Value, _OutputField -from django.db.models.fields import _ErrorMessagesToOverride, _FieldChoices, _ValidatorCallable +from django.db.models.fields import _ErrorMessagesMapping, _FieldChoices from django.db.models.lookups import Lookup from django.utils.functional import _StrOrPromise from typing_extensions import Self @@ -35,7 +36,7 @@ class SearchVectorField(Field[Any, Any]): db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> None: ... class SearchQueryField(Field[Any, Any]): @@ -61,7 +62,7 @@ class SearchQueryField(Field[Any, Any]): db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> None: ... class SearchVectorCombinable: diff --git a/django-stubs/contrib/redirects/admin.pyi b/django-stubs/contrib/redirects/admin.pyi index e10027bff..cf79e30f8 100644 --- a/django-stubs/contrib/redirects/admin.pyi +++ b/django-stubs/contrib/redirects/admin.pyi @@ -1,9 +1,9 @@ -from typing import Any +from typing import Any, ClassVar -import django.contrib.admin as admin +from django.contrib import admin -class RedirectAdmin(admin.ModelAdmin[Any]): - list_display: Any = ... - list_filter: Any = ... - search_fields: Any = ... - radio_fields: Any = ... +class RedirectAdmin(admin.ModelAdmin): + list_display: Any + list_filter: ClassVar[Any] + search_fields: ClassVar[Any] + radio_fields: ClassVar[Any] diff --git a/django-stubs/contrib/sitemaps/__init__.pyi b/django-stubs/contrib/sitemaps/__init__.pyi index bfe9245ee..a3d635b88 100644 --- a/django-stubs/contrib/sitemaps/__init__.pyi +++ b/django-stubs/contrib/sitemaps/__init__.pyi @@ -1,13 +1,13 @@ from collections.abc import Iterable, Mapping, Sequence from datetime import datetime -from typing import Any, Generic, TypeVar +from typing import Any, Generic from django.contrib.sites.models import Site from django.contrib.sites.requests import RequestSite from django.core.paginator import Paginator from django.db.models.base import Model from django.db.models.query import QuerySet -from typing_extensions import override +from typing_extensions import TypeVar, override _ItemT = TypeVar("_ItemT") diff --git a/django-stubs/contrib/sites/admin.pyi b/django-stubs/contrib/sites/admin.pyi index 98d6da142..9c4ad46f9 100644 --- a/django-stubs/contrib/sites/admin.pyi +++ b/django-stubs/contrib/sites/admin.pyi @@ -1,7 +1,7 @@ -from typing import Any +from typing import Any, ClassVar -import django.contrib.admin as admin +from django.contrib import admin -class SiteAdmin(admin.ModelAdmin[Any]): - list_display: Any = ... - search_fields: Any = ... +class SiteAdmin(admin.ModelAdmin): + list_display: Any + search_fields: ClassVar[Any] diff --git a/django-stubs/core/signing.pyi b/django-stubs/core/signing.pyi index 9732ad0a9..ea4f5a4fd 100644 --- a/django-stubs/core/signing.pyi +++ b/django-stubs/core/signing.pyi @@ -1,53 +1,79 @@ from datetime import timedelta -from typing import Any, Protocol +from typing import Any, Protocol, type_check_only + +from typing_extensions import override + +BASE62_ALPHABET: str class BadSignature(Exception): ... class SignatureExpired(BadSignature): ... +def b62_encode(s: int) -> str: ... +def b62_decode(s: str) -> int: ... def b64_encode(s: bytes) -> bytes: ... def b64_decode(s: bytes) -> bytes: ... -def base64_hmac(salt: str, value: bytes | str, key: bytes | str) -> str: ... -def get_cookie_signer(salt: str = ...) -> TimestampSigner: ... +def base64_hmac(salt: bytes | str, value: bytes | str, key: bytes | str, algorithm: str = "sha1") -> str: ... +def get_cookie_signer(salt: str = "django.core.signing.get_cookie_signer") -> TimestampSigner: ... +@type_check_only class Serializer(Protocol): def dumps(self, obj: Any) -> bytes: ... def loads(self, data: bytes) -> Any: ... class JSONSerializer: def dumps(self, obj: Any) -> bytes: ... - def loads(self, data: bytes) -> dict[str, int | str]: ... + def loads(self, data: bytes) -> Any: ... def dumps( obj: Any, - key: None = ..., - salt: str = ..., + key: bytes | str | None = None, + salt: bytes | str = "django.core.signing", serializer: type[Serializer] = ..., - compress: bool = ..., + compress: bool = False, ) -> str: ... def loads( s: str, - key: None = ..., - salt: str = ..., + key: bytes | str | None = None, + salt: bytes | str = "django.core.signing", serializer: type[Serializer] = ..., - max_age: int | timedelta | None = ..., + max_age: int | timedelta | None = None, + fallback_keys: list[str | bytes] | None = None, ) -> Any: ... class Signer: - key: str = ... - sep: str = ... - salt: str = ... - algorithm: str = ... + key: bytes | str + fallback_keys: list[bytes | str] + sep: str + salt: bytes | str + algorithm: str def __init__( self, - key: bytes | str | None = ..., - sep: str = ..., - salt: str | None = ..., - algorithm: str = ..., + *, + key: bytes | str | None = None, + sep: str = ":", + salt: bytes | str | None = None, + algorithm: str | None = None, + fallback_keys: list[bytes | str] | None = None, ) -> None: ... - def signature(self, value: bytes | str) -> str: ... + def signature(self, value: bytes | str, key: bytes | str | None = None) -> str: ... def sign(self, value: str) -> str: ... def unsign(self, signed_value: str) -> str: ... + def sign_object( + self, + obj: Any, + serializer: type[Serializer] = ..., + compress: bool = False, + ) -> str: ... + def unsign_object( + self, + signed_obj: str, + serializer: type[Serializer] = ..., + **kwargs: Any, + ) -> Any: ... class TimestampSigner(Signer): def timestamp(self) -> str: ... - def unsign(self, value: str, max_age: int | timedelta | None = ...) -> str: ... + @override + def sign(self, value: str) -> str: ... + @override + def unsign(self, value: str, max_age: int | timedelta | None = None) -> str: ... diff --git a/django-stubs/db/models/fields/__init__.pyi b/django-stubs/db/models/fields/__init__.pyi index 004dabb05..8ef9e05b5 100644 --- a/django-stubs/db/models/fields/__init__.pyi +++ b/django-stubs/db/models/fields/__init__.pyi @@ -3,27 +3,30 @@ import ipaddress import uuid from collections.abc import Callable, Iterable, Mapping, Sequence from datetime import date, datetime, time, timedelta -from typing import Any, Generic, Literal, TypeAlias, TypeVar, overload +from typing import Any, ClassVar, Generic, Literal, TypeAlias, TypeVar, overload from django.core.checks import CheckMessage from django.core.exceptions import FieldDoesNotExist as FieldDoesNotExist +from django.core.validators import _ValidatorCallable from django.db.models import IntegerChoices, Model, TextChoices from django.db.models.expressions import Col, Combinable, Func -from django.db.models.query_utils import RegisterLookupMixin +from django.db.models.query_utils import Q, RegisterLookupMixin from django.forms import Widget -from django.utils.functional import _StrOrPromise +from django.utils.choices import _Choice, _ChoiceNamedGroup, _ChoicesCallable +from django.utils.functional import _StrOrPromise, cached_property from typing_extensions import Self BLANK_CHOICE_DASH: list[tuple[str, str]] = ... -_Choice: TypeAlias = tuple[Any, _StrOrPromise] -_ChoiceNamedGroup: TypeAlias = tuple[str, Iterable[_Choice]] _ChoicesMapping: TypeAlias = Mapping[Any, _StrOrPromise | Mapping[Any, _StrOrPromise]] _LiteralFieldChoices: TypeAlias = Iterable[_Choice | _ChoiceNamedGroup] | _ChoicesMapping _FieldChoices: TypeAlias = _LiteralFieldChoices | Callable[[], _LiteralFieldChoices] -_ValidatorCallable: TypeAlias = Callable[..., None] -_ErrorMessagesToOverride: TypeAlias = dict[str, Any] +_LimitChoicesTo: TypeAlias = Q | dict[str, Any] +_LimitChoicesToCallable: TypeAlias = Callable[[], _LimitChoicesTo] +_AllLimitChoicesTo: TypeAlias = _LimitChoicesTo | _LimitChoicesToCallable | _ChoicesCallable # noqa: PYI047 +_ErrorMessagesMapping: TypeAlias = Mapping[str, _StrOrPromise] +_ErrorMessagesDict: TypeAlias = dict[str, _StrOrPromise] # __set__ value type _ST = TypeVar("_ST") @@ -63,7 +66,7 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]): column: str default: Any db_default: Any - error_messages: _ErrorMessagesToOverride + default_error_messages: ClassVar[_ErrorMessagesDict] def __set__(self, instance: Any, value: _ST) -> None: ... # class access @overload @@ -99,7 +102,9 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]): def has_default(self) -> bool: ... def get_default(self) -> Any: ... def check(self, **kwargs: Any) -> list[CheckMessage]: ... - @property + @cached_property + def error_messages(self) -> _ErrorMessagesDict: ... + @cached_property def validators(self) -> list[_ValidatorCallable]: ... def validate(self, value: Any, model_instance: Model) -> None: ... def run_validators(self, value: Any) -> None: ... @@ -140,7 +145,7 @@ class IntegerField(Field[_I | Combinable, _I], Generic[_I]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> IntegerField[int]: ... @overload def __new__( @@ -169,7 +174,7 @@ class IntegerField(Field[_I | Combinable, _I], Generic[_I]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> IntegerField[int | None]: ... class PositiveIntegerRelDbTypeMixin: @@ -203,7 +208,7 @@ class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_I]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> PositiveIntegerField[int]: ... @overload def __new__( @@ -232,7 +237,7 @@ class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_I]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> PositiveIntegerField[int | None]: ... class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_I]): @@ -263,7 +268,7 @@ class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_I]) db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> PositiveSmallIntegerField[int]: ... @overload def __new__( @@ -292,7 +297,7 @@ class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_I]) db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> PositiveSmallIntegerField[int | None]: ... class SmallIntegerField(IntegerField[_I]): @@ -323,7 +328,7 @@ class SmallIntegerField(IntegerField[_I]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> SmallIntegerField[int]: ... @overload def __new__( @@ -352,7 +357,7 @@ class SmallIntegerField(IntegerField[_I]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> SmallIntegerField[int | None]: ... class BigIntegerField(IntegerField[_I]): @@ -383,7 +388,7 @@ class BigIntegerField(IntegerField[_I]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> BigIntegerField[int]: ... @overload def __new__( @@ -412,7 +417,7 @@ class BigIntegerField(IntegerField[_I]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> BigIntegerField[int | None]: ... class PositiveBigIntegerField(IntegerField[_I]): @@ -443,7 +448,7 @@ class PositiveBigIntegerField(IntegerField[_I]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> PositiveBigIntegerField[int]: ... @overload def __new__( @@ -472,7 +477,7 @@ class PositiveBigIntegerField(IntegerField[_I]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> PositiveBigIntegerField[int | None]: ... _F = TypeVar("_F", bound=float | None) @@ -504,7 +509,7 @@ class FloatField(Field[_F | Combinable, _F], Generic[_F]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> FloatField[float]: ... @overload def __new__( @@ -532,7 +537,7 @@ class FloatField(Field[_F | Combinable, _F], Generic[_F]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> FloatField[float | None]: ... _DEC = TypeVar("_DEC", bound=decimal.Decimal | None) @@ -569,7 +574,7 @@ class DecimalField(Field[_DEC | Combinable, _DEC], Generic[_DEC]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> DecimalField[decimal.Decimal]: ... @overload def __new__( @@ -599,7 +604,7 @@ class DecimalField(Field[_DEC | Combinable, _DEC], Generic[_DEC]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> DecimalField[decimal.Decimal | None]: ... class AutoFieldMeta(type): ... @@ -633,7 +638,7 @@ class AutoField(AutoFieldMixin, IntegerField[int], metaclass=AutoFieldMeta): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> Self: ... class BigAutoField(AutoFieldMixin, BigIntegerField[int]): @@ -664,7 +669,7 @@ class BigAutoField(AutoFieldMixin, BigIntegerField[int]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> Self: ... class SmallAutoField(AutoFieldMixin, SmallIntegerField[int]): @@ -695,7 +700,7 @@ class SmallAutoField(AutoFieldMixin, SmallIntegerField[int]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> Self: ... _C = TypeVar("_C", bound=str | None) @@ -729,7 +734,7 @@ class CharField(Field[_C | Combinable, _C], Generic[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> CharField[str]: ... @overload def __new__( @@ -759,7 +764,7 @@ class CharField(Field[_C | Combinable, _C], Generic[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> CharField[str | None]: ... class CommaSeparatedIntegerField(CharField[_C]): ... @@ -792,7 +797,7 @@ class SlugField(CharField[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., allow_unicode: bool = ..., ) -> SlugField[str]: ... @overload @@ -822,7 +827,7 @@ class SlugField(CharField[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., allow_unicode: bool = ..., ) -> SlugField[str | None]: ... @@ -854,7 +859,7 @@ class EmailField(CharField[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> EmailField[str]: ... @overload def __new__( @@ -883,7 +888,7 @@ class EmailField(CharField[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> EmailField[str | None]: ... class URLField(CharField[_C]): @@ -914,7 +919,7 @@ class URLField(CharField[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> URLField[str]: ... @overload def __new__( @@ -943,7 +948,7 @@ class URLField(CharField[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> URLField[str | None]: ... class TextField(Field[_C | Combinable, _C], Generic[_C]): @@ -974,7 +979,7 @@ class TextField(Field[_C | Combinable, _C], Generic[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> TextField[str]: ... @overload def __new__( @@ -1003,7 +1008,7 @@ class TextField(Field[_C | Combinable, _C], Generic[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> TextField[str | None]: ... _B = TypeVar("_B", bound=bool | None) @@ -1036,7 +1041,7 @@ class BooleanField(Field[_B | Combinable, _B], Generic[_B]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> BooleanField[bool]: ... @overload def __new__( @@ -1065,7 +1070,7 @@ class BooleanField(Field[_B | Combinable, _B], Generic[_B]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> BooleanField[bool | None]: ... NullBooleanField: TypeAlias = BooleanField[bool | None] @@ -1097,7 +1102,7 @@ class IPAddressField(Field[_C | Combinable, _C], Generic[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> IPAddressField[str]: ... @overload def __new__( @@ -1125,7 +1130,7 @@ class IPAddressField(Field[_C | Combinable, _C], Generic[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> IPAddressField[str | None]: ... class GenericIPAddressField( @@ -1160,7 +1165,7 @@ class GenericIPAddressField( db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> GenericIPAddressField[str]: ... @overload def __new__( @@ -1190,7 +1195,7 @@ class GenericIPAddressField( db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> GenericIPAddressField[str | None]: ... _DD = TypeVar("_DD", bound=date | None) @@ -1229,7 +1234,7 @@ class DateField(DateTimeCheckMixin, Field[_DD | Combinable, _DD]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> DateField[date]: ... @overload def __new__( @@ -1259,7 +1264,7 @@ class DateField(DateTimeCheckMixin, Field[_DD | Combinable, _DD]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> DateField[date | None]: ... _TM = TypeVar("_TM", bound=time | None) @@ -1296,7 +1301,7 @@ class TimeField(DateTimeCheckMixin, Field[_TM | Combinable, _TM], Generic[_TM]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> TimeField[time]: ... @overload def __new__( @@ -1326,7 +1331,7 @@ class TimeField(DateTimeCheckMixin, Field[_TM | Combinable, _TM], Generic[_TM]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> TimeField[time | None]: ... _DT = TypeVar("_DT", bound=datetime | None) @@ -1363,7 +1368,7 @@ class DateTimeField(DateField[_DT]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> DateTimeField[datetime]: ... @overload def __new__( @@ -1393,7 +1398,7 @@ class DateTimeField(DateField[_DT]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> DateTimeField[datetime | None]: ... _U = TypeVar("_U", bound=uuid.UUID | None) @@ -1425,7 +1430,7 @@ class UUIDField(Field[str | _U, _U], Generic[_U]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> UUIDField[uuid.UUID]: ... @overload def __new__( @@ -1453,7 +1458,7 @@ class UUIDField(Field[str | _U, _U], Generic[_U]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> UUIDField[uuid.UUID | None]: ... class FilePathField(Field[_C, _C], Generic[_C]): @@ -1493,7 +1498,7 @@ class FilePathField(Field[_C, _C], Generic[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> FilePathField[str]: ... @overload def __new__( @@ -1526,7 +1531,7 @@ class FilePathField(Field[_C, _C], Generic[_C]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> FilePathField[str | None]: ... _BIN = TypeVar("_BIN", bound=bytes | None) @@ -1558,7 +1563,7 @@ class BinaryField(Field[_BIN | bytearray | memoryview, _BIN], Generic[_BIN]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> BinaryField[bytes]: ... @overload def __new__( @@ -1586,7 +1591,7 @@ class BinaryField(Field[_BIN | bytearray | memoryview, _BIN], Generic[_BIN]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> BinaryField[bytes | None]: ... _TD = TypeVar("_TD", bound=timedelta | None) @@ -1618,7 +1623,7 @@ class DurationField(Field[_TD, _TD], Generic[_TD]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> DurationField[timedelta]: ... @overload def __new__( @@ -1646,5 +1651,5 @@ class DurationField(Field[_TD, _TD], Generic[_TD]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> DurationField[timedelta | None]: ... diff --git a/django-stubs/db/models/fields/composite.pyi b/django-stubs/db/models/fields/composite.pyi index 1aa1fd05a..292669469 100644 --- a/django-stubs/db/models/fields/composite.pyi +++ b/django-stubs/db/models/fields/composite.pyi @@ -2,8 +2,9 @@ from collections.abc import Iterable, Iterator, Mapping from typing import Any, Literal from django.contrib.contenttypes.fields import GenericForeignKey +from django.core.validators import _ValidatorCallable from django.db.models.base import Model -from django.db.models.fields import NOT_PROVIDED, Field, _LiteralFieldChoices, _ValidatorCallable +from django.db.models.fields import NOT_PROVIDED, Field, _LiteralFieldChoices from django.db.models.fields.reverse_related import ForeignObjectRel from django.utils.functional import _StrOrPromise, cached_property diff --git a/django-stubs/db/models/fields/files.pyi b/django-stubs/db/models/fields/files.pyi index 9fc400c8f..303d7743c 100644 --- a/django-stubs/db/models/fields/files.pyi +++ b/django-stubs/db/models/fields/files.pyi @@ -4,8 +4,9 @@ from typing import Any, overload from django.core.files.base import File from django.core.files.images import ImageFile from django.core.files.storage import FileSystemStorage, Storage +from django.core.validators import _ValidatorCallable from django.db.models.base import Model -from django.db.models.fields import _GT, Field, _ErrorMessagesToOverride, _ValidatorCallable +from django.db.models.fields import _GT, Field, _ErrorMessagesMapping from django.utils.functional import _StrOrPromise from typing_extensions import Self @@ -60,7 +61,7 @@ class FileField(Field[FileDescriptor, FileDescriptor]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> Self: ... # class access @overload # type: ignore[override] @@ -109,7 +110,7 @@ class ImageField(FileField): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> Self: ... # class access @overload # type: ignore[override] diff --git a/django-stubs/db/models/fields/generated.pyi b/django-stubs/db/models/fields/generated.pyi index 607a353ae..b8d764980 100644 --- a/django-stubs/db/models/fields/generated.pyi +++ b/django-stubs/db/models/fields/generated.pyi @@ -1,12 +1,13 @@ from collections.abc import Iterable from typing import Any, Literal, TypeVar, overload +from django.core.validators import _ValidatorCallable from django.db.models import Expression, ForeignObjectRel from django.db.models.expressions import Col, Combinable from django.utils.functional import _StrOrPromise from typing_extensions import Never -from . import Field, _ErrorMessagesToOverride, _ValidatorCallable +from . import Field, _ErrorMessagesMapping from .mixins import CheckFieldDefaultMixin _GT = TypeVar("_GT", bound=Any | None) @@ -44,7 +45,7 @@ class GeneratedField(CheckFieldDefaultMixin, Field[Never, _GT]): db_tablespace: str | None = ..., auto_created: bool = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., db_comment: str | None = ..., ) -> GeneratedField[_GT]: ... @overload @@ -75,7 +76,7 @@ class GeneratedField(CheckFieldDefaultMixin, Field[Never, _GT]): db_tablespace: str | None = ..., auto_created: bool = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., db_comment: str | None = ..., ) -> GeneratedField[_GT | None]: ... @property diff --git a/django-stubs/db/models/fields/json.pyi b/django-stubs/db/models/fields/json.pyi index e0faf5ac6..48356cac4 100644 --- a/django-stubs/db/models/fields/json.pyi +++ b/django-stubs/db/models/fields/json.pyi @@ -2,19 +2,19 @@ import json from collections.abc import Callable, Iterable from typing import Any, Literal, TypeVar, overload +from django.core.validators import _ValidatorCallable from django.db.models import lookups from django.db.models.expressions import Combinable, Func from django.db.models.lookups import PostgresOperatorLookup, Transform from django.utils.functional import _StrOrPromise from typing_extensions import Self -from . import Field, _ErrorMessagesToOverride, _ValidatorCallable +from . import Field, _ErrorMessagesMapping from .mixins import CheckFieldDefaultMixin _A = TypeVar("_A", bound=Any | None) class JSONField(CheckFieldDefaultMixin, Field[_A | Combinable, _A]): - default_error_messages: Any = ... encoder: type[json.JSONEncoder] = ... decoder: type[json.JSONEncoder] = ... def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: ... @@ -48,7 +48,7 @@ class JSONField(CheckFieldDefaultMixin, Field[_A | Combinable, _A]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> JSONField[_A]: ... @overload def __new__( @@ -78,7 +78,7 @@ class JSONField(CheckFieldDefaultMixin, Field[_A | Combinable, _A]): db_comment: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., - error_messages: _ErrorMessagesToOverride | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., ) -> JSONField[_A | None]: ... class DataContains(PostgresOperatorLookup): diff --git a/django-stubs/db/models/sql/datastructures.pyi b/django-stubs/db/models/sql/datastructures.pyi index c63a60a10..43c595c32 100644 --- a/django-stubs/db/models/sql/datastructures.pyi +++ b/django-stubs/db/models/sql/datastructures.pyi @@ -17,7 +17,7 @@ class Join: parent_alias: str table_alias: str | None join_type: str - join_cols: tuple[Any, ...] + join_cols: tuple join_field: FieldCacheMixin nullable: bool filtered_relation: FilteredRelation | None diff --git a/django-stubs/forms/__init__.pyi b/django-stubs/forms/__init__.pyi index 29b9310a1..fa6d23eff 100644 --- a/django-stubs/forms/__init__.pyi +++ b/django-stubs/forms/__init__.pyi @@ -1,8 +1,6 @@ from django.core.exceptions import ValidationError as ValidationError -from . import utils as utils from .boundfield import BoundField as BoundField -from .boundfield import BoundWidget as BoundWidget from .fields import BooleanField as BooleanField from .fields import CharField as CharField from .fields import ChoiceField as ChoiceField @@ -36,46 +34,16 @@ from .forms import Form as Form from .formsets import BaseFormSet as BaseFormSet from .formsets import all_valid as all_valid from .formsets import formset_factory as formset_factory +from .models import ALL_FIELDS as ALL_FIELDS from .models import BaseInlineFormSet as BaseInlineFormSet from .models import BaseModelForm as BaseModelForm from .models import BaseModelFormSet as BaseModelFormSet -from .models import InlineForeignKeyField as InlineForeignKeyField from .models import ModelChoiceField as ModelChoiceField -from .models import ModelChoiceIterator as ModelChoiceIterator from .models import ModelForm as ModelForm -from .models import ModelFormMetaclass as ModelFormMetaclass -from .models import ModelFormOptions as ModelFormOptions from .models import ModelMultipleChoiceField as ModelMultipleChoiceField from .models import fields_for_model as fields_for_model from .models import inlineformset_factory as inlineformset_factory from .models import model_to_dict as model_to_dict from .models import modelform_factory as modelform_factory from .models import modelformset_factory as modelformset_factory -from .widgets import CheckboxInput as CheckboxInput -from .widgets import CheckboxSelectMultiple as CheckboxSelectMultiple -from .widgets import ChoiceWidget as ChoiceWidget -from .widgets import ClearableFileInput as ClearableFileInput -from .widgets import DateInput as DateInput -from .widgets import DateTimeBaseInput as DateTimeBaseInput -from .widgets import DateTimeInput as DateTimeInput -from .widgets import EmailInput as EmailInput -from .widgets import FileInput as FileInput -from .widgets import HiddenInput as HiddenInput -from .widgets import Input as Input -from .widgets import Media as Media -from .widgets import MultipleHiddenInput as MultipleHiddenInput -from .widgets import MultiWidget as MultiWidget -from .widgets import NullBooleanSelect as NullBooleanSelect -from .widgets import NumberInput as NumberInput -from .widgets import PasswordInput as PasswordInput -from .widgets import RadioSelect as RadioSelect -from .widgets import Select as Select -from .widgets import SelectDateWidget as SelectDateWidget -from .widgets import SelectMultiple as SelectMultiple -from .widgets import SplitDateTimeWidget as SplitDateTimeWidget -from .widgets import SplitHiddenDateTimeWidget as SplitHiddenDateTimeWidget -from .widgets import Textarea as Textarea -from .widgets import TextInput as TextInput -from .widgets import TimeInput as TimeInput -from .widgets import URLInput as URLInput -from .widgets import Widget as Widget +from .widgets import * diff --git a/django-stubs/forms/boundfield.pyi b/django-stubs/forms/boundfield.pyi index 46d78e628..12d220216 100644 --- a/django-stubs/forms/boundfield.pyi +++ b/django-stubs/forms/boundfield.pyi @@ -1,69 +1,95 @@ -from collections.abc import Iterator -from typing import Any +from collections.abc import Iterable, Iterator +from typing import Any, TypeAlias, overload from django.forms.fields import Field from django.forms.forms import BaseForm -from django.forms.renderers import DjangoTemplates -from django.forms.utils import ErrorList +from django.forms.renderers import BaseRenderer +from django.forms.utils import ErrorList, RenderableFieldMixin from django.forms.widgets import Widget -from django.utils.functional import _StrOrPromise -from django.utils.safestring import SafeText +from django.utils.functional import _StrOrPromise, cached_property +from django.utils.safestring import SafeString +from typing_extensions import override -class BoundField: - initial: Any - form: BaseForm = ... - field: Field = ... - name: str = ... - html_name: str = ... - html_initial_name: str = ... - html_initial_id: str = ... - label: _StrOrPromise = ... - help_text: _StrOrPromise = ... +_AttrsT: TypeAlias = dict[str, str | bool] + +class BoundField(RenderableFieldMixin): + form: BaseForm + field: Field + name: str + html_name: str + html_initial_name: str + html_initial_id: str + label: _StrOrPromise + help_text: _StrOrPromise + renderer: BaseRenderer def __init__(self, form: BaseForm, field: Field, name: str) -> None: ... - @property + @cached_property def subwidgets(self) -> list[BoundWidget]: ... def __bool__(self) -> bool: ... def __iter__(self) -> Iterator[BoundWidget]: ... def __len__(self) -> int: ... - def __getitem__(self, idx: int | slice | str) -> list[BoundWidget] | BoundWidget: ... + @overload + def __getitem__(self, idx: int | str) -> BoundWidget: ... + @overload + def __getitem__(self, idx: slice) -> list[BoundWidget]: ... @property def errors(self) -> ErrorList: ... + @property + def template_name(self) -> str: ... + @override + def get_context(self) -> dict[str, Any]: ... + @override def as_widget( - self, - widget: Widget | None = ..., - attrs: None = ..., - only_initial: bool = ..., - ) -> SafeText: ... - def as_text(self, attrs: None = ..., **kwargs: Any) -> SafeText: ... - def as_textarea(self, attrs: None = ..., **kwargs: Any) -> SafeText: ... - def as_hidden(self, attrs: None = ..., **kwargs: Any) -> SafeText: ... + self, widget: Widget | None = None, attrs: _AttrsT | None = None, only_initial: bool = False + ) -> SafeString: ... + def as_text(self, attrs: _AttrsT | None = None, **kwargs: Any) -> SafeString: ... + @override + def __html__(self) -> SafeString: ... + def as_textarea(self, attrs: _AttrsT | None = None, **kwargs: Any) -> SafeString: ... + @override + def as_hidden(self, attrs: _AttrsT | None = None, **kwargs: Any) -> SafeString: ... @property def data(self) -> Any: ... def value(self) -> Any: ... def label_tag( self, - contents: str | None = ..., - attrs: dict[str, str] | None = ..., - label_suffix: str | None = ..., - ) -> SafeText: ... - def css_classes(self, extra_classes: None = ...) -> str: ... + contents: str | None = None, + attrs: _AttrsT | None = None, + label_suffix: str | None = None, + tag: str | None = None, + ) -> SafeString: ... + def legend_tag( + self, contents: str | None = None, attrs: _AttrsT | None = None, label_suffix: str | None = None + ) -> SafeString: ... + def css_classes(self, extra_classes: str | Iterable[str] | None = None) -> str: ... @property def is_hidden(self) -> bool: ... @property def auto_id(self) -> str: ... @property def id_for_label(self) -> str: ... - def build_widget_attrs(self, attrs: dict[str, str], widget: Widget | None = ...) -> dict[str, bool | str]: ... + @cached_property + def initial(self) -> Any: ... + def build_widget_attrs(self, attrs: _AttrsT, widget: Widget | None = None) -> _AttrsT: ... + @property + def aria_describedby(self) -> str | None: ... + @property + def widget_type(self) -> str: ... + @property + def use_fieldset(self) -> bool: ... class BoundWidget: - parent_widget: Widget = ... - data: dict[str, Any] = ... - renderer: DjangoTemplates = ... - def __init__(self, parent_widget: Widget, data: dict[str, Any], renderer: DjangoTemplates) -> None: ... - def tag(self, wrap_label: bool = ...) -> SafeText: ... + parent_widget: Widget + data: dict[str, Any] + renderer: BaseRenderer + def __init__(self, parent_widget: Widget, data: dict[str, Any], renderer: BaseRenderer) -> None: ... + def tag(self, wrap_label: bool = False) -> SafeString: ... @property def template_name(self) -> str: ... @property def id_for_label(self) -> str: ... @property def choice_label(self) -> str: ... + def __html__(self) -> SafeString: ... + +__all__ = ("BoundField",) diff --git a/django-stubs/forms/fields.pyi b/django-stubs/forms/fields.pyi index 849a7e923..b4e1723c5 100644 --- a/django-stubs/forms/fields.pyi +++ b/django-stubs/forms/fields.pyi @@ -1,51 +1,63 @@ -from collections.abc import Callable, Iterable, Sequence -from datetime import datetime, timedelta +import datetime +from collections.abc import Callable, Collection, Iterator, Sequence +from decimal import Decimal from re import Pattern -from typing import Any, TypeAlias +from typing import Any, ClassVar, Protocol, TypeAlias, type_check_only +from uuid import UUID -from django.core.validators import BaseValidator +from django.core.files import File +from django.core.validators import _ValidatorCallable +from django.db.models.fields import _ErrorMessagesDict, _ErrorMessagesMapping from django.forms.boundfield import BoundField from django.forms.forms import BaseForm from django.forms.widgets import Widget +from django.utils.choices import CallableChoiceIterator, _ChoicesCallable, _ChoicesInput +from django.utils.datastructures import _PropertyDescriptor from django.utils.functional import _StrOrPromise +from typing_extensions import Self, override -_Choice: TypeAlias = tuple[Any, Any] -_ChoiceNamedGroup: TypeAlias = tuple[str, Iterable[_Choice]] -_FieldChoices: TypeAlias = Iterable[_Choice | _ChoiceNamedGroup] +# Problem: attribute `widget` is always of type `Widget` after field instantiation. +# However, on class level it can be set to `Type[Widget]` too. +# If we annotate it as `Widget | Type[Widget]`, every code that uses field +# instances will not typecheck. +# If we annotate it as `Widget`, any widget subclasses that do e.g. +# `widget = Select` will not typecheck. +# `Any` gives too much freedom, but does not create false positives. +_ClassLevelWidgetT: TypeAlias = Any class Field: initial: Any label: _StrOrPromise | None required: bool - widget: type[Widget] | Widget = ... - hidden_widget: Any = ... - default_validators: Any = ... - default_error_messages: Any = ... - empty_values: Any = ... - show_hidden_initial: bool = ... - help_text: _StrOrPromise = ... - disabled: bool = ... - label_suffix: Any | None = ... - localize: bool = ... - error_messages: Any = ... - validators: list[BaseValidator] = ... - max_length: int | str | None = ... - choices: _FieldChoices = ... - base_field: Field + widget: _ClassLevelWidgetT + hidden_widget: type[Widget] + default_validators: list[_ValidatorCallable] + default_error_messages: ClassVar[_ErrorMessagesDict] + empty_values: Sequence[Any] + show_hidden_initial: bool + help_text: _StrOrPromise + disabled: bool + label_suffix: str | None + localize: bool + error_messages: _ErrorMessagesDict + validators: list[_ValidatorCallable] + bound_field_class: type[BoundField] | None def __init__( self, *, - required: bool = ..., - widget: Widget | type[Widget] | None = ..., - label: _StrOrPromise | None = ..., - initial: Any | None = ..., - help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., - show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., - localize: bool = ..., - disabled: bool = ..., - label_suffix: Any | None = ..., + required: bool = True, + widget: Widget | type[Widget] | None = None, + label: _StrOrPromise | None = None, + initial: Any | None = None, + help_text: _StrOrPromise = "", + error_messages: _ErrorMessagesMapping | None = None, + show_hidden_initial: bool = False, + validators: Sequence[_ValidatorCallable] = (), + localize: bool = False, + disabled: bool = False, + label_suffix: str | None = None, + template_name: str | None = None, + bound_field_class: type[BoundField] | None = None, ) -> None: ... def prepare_value(self, value: Any) -> Any: ... def to_python(self, value: Any | None) -> Any | None: ... @@ -53,56 +65,94 @@ class Field: def run_validators(self, value: Any) -> None: ... def clean(self, value: Any) -> Any: ... def bound_data(self, data: Any, initial: Any) -> Any: ... - def widget_attrs(self, widget: Widget) -> Any: ... - def has_changed(self, initial: Any, data: Any) -> bool: ... + def widget_attrs(self, widget: Widget) -> dict[str, Any]: ... + def has_changed(self, initial: Any | None, data: Any | None) -> bool: ... def get_bound_field(self, form: BaseForm, field_name: str) -> BoundField: ... - def deconstruct(self) -> Any: ... + def __deepcopy__(self, memo: dict[int, Any]) -> Self: ... class CharField(Field): - min_length: int | str | None = ... - strip: bool = ... - empty_value: str | None = ... + max_length: int | None + min_length: int | None + strip: bool + empty_value: str | None def __init__( self, - max_length: Any | None = ..., - min_length: Any | None = ..., - strip: bool = ..., - empty_value: str | None = ..., + *, + max_length: int | None = None, + min_length: int | None = None, + strip: bool = True, + empty_value: str | None = "", required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... + @override + def to_python(self, value: Any | None) -> str | None: ... + @override + def widget_attrs(self, widget: Widget) -> dict[str, Any]: ... class IntegerField(Field): - max_value: Any | None - min_value: Any | None - re_decimal: Any = ... + widget: _ClassLevelWidgetT + max_value: int | Callable[[], int] | None + min_value: int | Callable[[], int] | None + step_size: int | Callable[[], int] | None + re_decimal: Any def __init__( self, - max_value: Any | None = ..., - min_value: Any | None = ..., + *, + max_value: int | Callable[[], int] | None = None, + min_value: int | Callable[[], int] | None = None, + step_size: int | Callable[[], int] | None = None, required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... + @override + def to_python(self, value: Any | None) -> int | None: ... + @override + def widget_attrs(self, widget: Widget) -> dict[str, Any]: ... -class FloatField(IntegerField): ... +class FloatField(IntegerField): + def __init__( + self, + *, + max_value: int | float | None = None, + min_value: int | float | None = None, + step_size: int | float | None = None, + required: bool = ..., + widget: Widget | type[Widget] | None = ..., + label: _StrOrPromise | None = ..., + initial: Any | None = ..., + help_text: _StrOrPromise = ..., + error_messages: _ErrorMessagesMapping | None = ..., + show_hidden_initial: bool = ..., + validators: Sequence[_ValidatorCallable] = ..., + localize: bool = ..., + disabled: bool = ..., + label_suffix: str | None = ..., + ) -> None: ... + @override + def to_python(self, value: Any | None) -> float | None: ... # type: ignore[override] + @override + def validate(self, value: float) -> None: ... + @override + def widget_attrs(self, widget: Widget) -> dict[str, Any]: ... class DecimalField(IntegerField): decimal_places: int | None @@ -110,56 +160,92 @@ class DecimalField(IntegerField): def __init__( self, *, - max_value: Any | None = ..., - min_value: Any | None = ..., - max_digits: Any | None = ..., - decimal_places: Any | None = ..., + max_value: Decimal | int | float | None = None, + min_value: Decimal | int | float | None = None, + max_digits: int | None = None, + decimal_places: int | None = None, + step_size: Decimal | int | float | None = ..., required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... + @override + def to_python(self, value: Any | None) -> Decimal | None: ... # type: ignore[override] + @override + def validate(self, value: Decimal) -> None: ... + @override + def widget_attrs(self, widget: Widget) -> dict[str, Any]: ... class BaseTemporalField(Field): - input_formats: Any = ... + input_formats: Any def __init__( self, - input_formats: Any | None = ..., + *, + input_formats: Any | None = None, required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... - def strptime(self, value: Any, format: str) -> Any: ... + @override + def to_python(self, value: str | None) -> Any | None: ... + def strptime(self, value: str, format: str) -> Any: ... + +class DateField(BaseTemporalField): + widget: _ClassLevelWidgetT + @override + def to_python(self, value: None | str | datetime.datetime | datetime.date) -> datetime.date | None: ... + @override + def strptime(self, value: str, format: str) -> datetime.date: ... + +class TimeField(BaseTemporalField): + widget: _ClassLevelWidgetT + @override + def to_python(self, value: None | str | datetime.time) -> datetime.time | None: ... + @override + def strptime(self, value: str, format: str) -> datetime.time: ... + +class DateTimeFormatsIterator: + def __iter__(self) -> Iterator[str]: ... -class DateField(BaseTemporalField): ... -class TimeField(BaseTemporalField): ... -class DateTimeField(BaseTemporalField): ... +class DateTimeField(BaseTemporalField): + widget: _ClassLevelWidgetT + @override + def prepare_value(self, value: Any) -> Any: ... + @override + def to_python(self, value: None | str | datetime.datetime | datetime.date) -> datetime.datetime | None: ... + @override + def strptime(self, value: str, format: str) -> datetime.datetime: ... class DurationField(Field): - def prepare_value(self, value: timedelta | str | None) -> str | None: ... + @override + def prepare_value(self, value: datetime.timedelta | str | None) -> str | None: ... + @override + def to_python(self, value: Any | None) -> datetime.timedelta | None: ... class RegexField(CharField): - regex: str = ... + regex: _PropertyDescriptor[str | Pattern[str], Pattern[str]] def __init__( self, regex: str | Pattern[str], - max_length: Any | None = ..., - min_length: Any | None = ..., + *, + max_length: int | None = ..., + min_length: int | None = ..., strip: bool = ..., empty_value: str | None = ..., required: bool = ..., @@ -167,146 +253,259 @@ class RegexField(CharField): label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... -class EmailField(CharField): ... +class EmailField(CharField): + widget: _ClassLevelWidgetT + def __init__( + self, + *, + max_length: int | None = ..., + min_length: int | None = ..., + strip: bool = ..., + empty_value: str | None = ..., + required: bool = ..., + widget: Widget | type[Widget] | None = ..., + label: _StrOrPromise | None = ..., + initial: Any | None = ..., + help_text: _StrOrPromise = ..., + error_messages: _ErrorMessagesMapping | None = ..., + show_hidden_initial: bool = ..., + validators: Sequence[_ValidatorCallable] = ..., + localize: bool = ..., + disabled: bool = ..., + label_suffix: str | None = ..., + ) -> None: ... class FileField(Field): - allow_empty_file: bool = ... + widget: _ClassLevelWidgetT + allow_empty_file: bool + max_length: int | None def __init__( self, - max_length: Any | None = ..., - allow_empty_file: bool = ..., + *, + max_length: int | None = None, + allow_empty_file: bool = False, + required: bool = ..., + widget: Widget | type[Widget] | None = ..., + label: _StrOrPromise | None = ..., + initial: Any | None = ..., + help_text: _StrOrPromise = ..., + error_messages: _ErrorMessagesMapping | None = ..., + show_hidden_initial: bool = ..., + validators: Sequence[_ValidatorCallable] = ..., + localize: bool = ..., + disabled: bool = ..., + label_suffix: str | None = ..., + ) -> None: ... + @override + def to_python(self, data: File | None) -> File | None: ... + @override + def clean(self, data: Any, initial: Any | None = None) -> Any: ... + @override + def bound_data(self, _: Any | None, initial: Any) -> Any: ... + @override + def has_changed(self, initial: Any | None, data: Any | None) -> bool: ... + +class ImageField(FileField): + @override + def to_python(self, data: File | None) -> File | None: ... + @override + def widget_attrs(self, widget: Widget) -> dict[str, Any]: ... + +class URLField(CharField): + widget: _ClassLevelWidgetT + def __init__( + self, + *, + max_length: int | None = ..., + min_length: int | None = ..., + strip: bool = ..., + empty_value: str | None = ..., required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., + assume_scheme: str | None = None, ) -> None: ... - def clean(self, data: Any, initial: Any | None = ...) -> Any: ... + @override + def to_python(self, value: Any | None) -> str | None: ... -class ImageField(FileField): ... -class URLField(CharField): ... -class BooleanField(Field): ... -class NullBooleanField(BooleanField): ... +class BooleanField(Field): + widget: _ClassLevelWidgetT + @override + def to_python(self, value: Any | None) -> bool: ... + @override + def validate(self, value: Any) -> None: ... + @override + def has_changed(self, initial: Any | None, data: Any | None) -> bool: ... -class CallableChoiceIterator: - choices_func: Callable[..., Any] = ... - def __init__(self, choices_func: Callable[..., Any]) -> None: ... - def __iter__(self) -> None: ... +class NullBooleanField(BooleanField): + widget: _ClassLevelWidgetT + @override + def to_python(self, value: Any | None) -> bool | None: ... # type: ignore[override] + @override + def validate(self, value: Any) -> None: ... class ChoiceField(Field): + choices: _PropertyDescriptor[ + _ChoicesInput | _ChoicesCallable | CallableChoiceIterator, + _ChoicesInput | CallableChoiceIterator, + ] + widget: _ClassLevelWidgetT def __init__( self, - choices: _FieldChoices | Callable[[], _FieldChoices] = ..., + *, + choices: _ChoicesInput | _ChoicesCallable = (), required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... - def valid_value(self, value: str) -> bool: ... + @override + def __deepcopy__(self, memo: dict[int, Any]) -> Self: ... + # Real return type of `to_python` is `str`, but it results in errors when + # subclassing `ModelChoiceField`: `# type: ignore[override]` is not inherited + @override + def to_python(self, value: Any | None) -> Any: ... + @override + def validate(self, value: Any) -> None: ... + def valid_value(self, value: Any) -> bool: ... + +@type_check_only +class _CoerceCallable(Protocol): + def __call__(self, value: Any, /) -> Any: ... class TypedChoiceField(ChoiceField): - coerce: Callable[..., Any] | type[Any] = ... - empty_value: str | None = ... + coerce: _CoerceCallable + empty_value: str | None def __init__( self, - coerce: Any = ..., - empty_value: str | None = ..., - choices: Any = ..., + *, + coerce: _CoerceCallable = ..., + empty_value: str | None = "", + choices: _ChoicesInput | _ChoicesCallable = ..., required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... + @override + def clean(self, value: Any) -> Any: ... -class MultipleChoiceField(ChoiceField): ... +class MultipleChoiceField(ChoiceField): + widget: _ClassLevelWidgetT + hidden_widget: type[Widget] + @override + def to_python(self, value: Any | None) -> list[str]: ... + @override + def validate(self, value: Any) -> None: ... + @override + def has_changed(self, initial: Collection[Any] | None, data: Collection[Any] | None) -> bool: ... class TypedMultipleChoiceField(MultipleChoiceField): - coerce: Callable[..., Any] | type[float] = ... - empty_value: list[Any] | None = ... + coerce: _CoerceCallable + empty_value: list[Any] | None def __init__( self, - coerce: Any = ..., - empty_value: str | None = ..., - choices: Any = ..., + *, + coerce: _CoerceCallable = ..., + empty_value: list[Any] | None = ..., + choices: _ChoicesInput | _ChoicesCallable = ..., required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... + @override + def clean(self, value: Any) -> Any: ... + @override + def validate(self, value: Any) -> None: ... class ComboField(Field): - fields: Any = ... + fields: Sequence[Field] def __init__( self, fields: Sequence[Field], + *, required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... + @override + def clean(self, value: Any) -> Any: ... class MultiValueField(Field): - require_all_fields: bool = ... - fields: Any = ... + require_all_fields: bool + fields: Sequence[Field] def __init__( self, fields: Sequence[Field], - require_all_fields: bool = ..., + *, + require_all_fields: bool = True, required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... + @override + def __deepcopy__(self, memo: dict[int, Any]) -> Self: ... + @override + def validate(self, value: Any) -> None: ... + @override + def clean(self, value: Any) -> Any: ... def compress(self, data_list: Any) -> Any: ... + @override + def has_changed(self, initial: Any | None, data: Any | None) -> bool: ... class FilePathField(ChoiceField): allow_files: bool @@ -314,33 +513,37 @@ class FilePathField(ChoiceField): match: str | None path: str recursive: bool - match_re: Any = ... + match_re: Pattern[str] | None def __init__( self, path: str, - match: Any | None = ..., - recursive: bool = ..., - allow_files: bool = ..., - allow_folders: bool = ..., - choices: Any = ..., + *, + match: str | None = None, + recursive: bool = False, + allow_files: bool = True, + allow_folders: bool = False, + choices: _ChoicesInput | _ChoicesCallable = ..., required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... class SplitDateTimeField(MultiValueField): + widget: _ClassLevelWidgetT + hidden_widget: type[Widget] def __init__( self, - input_date_formats: Any | None = ..., - input_time_formats: Any | None = ..., + *, + input_date_formats: Any | None = None, + input_time_formats: Any | None = None, fields: Sequence[Field] = ..., require_all_fields: bool = ..., required: bool = ..., @@ -348,63 +551,112 @@ class SplitDateTimeField(MultiValueField): label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... - def compress(self, data_list: list[datetime | None]) -> datetime | None: ... + @override + def compress(self, data_list: tuple[datetime.date, datetime.time] | None) -> datetime.datetime | None: ... class GenericIPAddressField(CharField): - unpack_ipv4: bool = ... + unpack_ipv4: bool def __init__( self, - protocol: str = ..., - unpack_ipv4: bool = ..., + *, + protocol: str = "both", + unpack_ipv4: bool = False, required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... + @override + def to_python(self, value: Any) -> str: ... class SlugField(CharField): - allow_unicode: bool = ... + allow_unicode: bool def __init__( self, - allow_unicode: bool = ..., + *, + allow_unicode: bool = False, + max_length: Any | None = ..., + min_length: Any | None = ..., + strip: bool = ..., + empty_value: str | None = ..., required: bool = ..., widget: Widget | type[Widget] | None = ..., label: _StrOrPromise | None = ..., initial: Any | None = ..., help_text: _StrOrPromise = ..., - error_messages: Any | None = ..., + error_messages: _ErrorMessagesMapping | None = ..., show_hidden_initial: bool = ..., - validators: Sequence[Any] = ..., + validators: Sequence[_ValidatorCallable] = ..., localize: bool = ..., disabled: bool = ..., - label_suffix: Any | None = ..., + label_suffix: str | None = ..., ) -> None: ... -class UUIDField(CharField): ... +class UUIDField(CharField): + @override + def prepare_value(self, value: Any | None) -> Any | None: ... + @override + def to_python(self, value: Any) -> UUID | None: ... # type: ignore[override] + class InvalidJSONInput(str): ... class JSONString(str): ... class JSONField(CharField): - default_error_messages: Any = ... - widget: Any = ... - encoder: Any = ... - decoder: Any = ... - def __init__(self, encoder: Any | None = ..., decoder: Any | None = ..., **kwargs: Any) -> None: ... + default_error_messages: ClassVar[_ErrorMessagesDict] + widget: _ClassLevelWidgetT + encoder: Any + decoder: Any + def __init__(self, encoder: Any | None = None, decoder: Any | None = None, **kwargs: Any) -> None: ... + @override def to_python(self, value: Any) -> Any: ... + @override def bound_data(self, data: Any, initial: Any) -> Any: ... - def prepare_value(self, value: Any) -> Any: ... - def has_changed(self, initial: Any, data: Any) -> Any: ... + @override + def prepare_value(self, value: Any) -> str: ... + @override + def has_changed(self, initial: Any | None, data: Any | None) -> bool: ... + +__all__ = ( + "BooleanField", + "CharField", + "ChoiceField", + "ComboField", + "DateField", + "DateTimeField", + "DecimalField", + "DurationField", + "EmailField", + "Field", + "FileField", + "FilePathField", + "FloatField", + "GenericIPAddressField", + "ImageField", + "IntegerField", + "JSONField", + "MultiValueField", + "MultipleChoiceField", + "NullBooleanField", + "RegexField", + "SlugField", + "SplitDateTimeField", + "TimeField", + "TypedChoiceField", + "TypedMultipleChoiceField", + "URLField", + "UUIDField", +) diff --git a/django-stubs/forms/forms.pyi b/django-stubs/forms/forms.pyi index 2763b2924..95022ed6d 100644 --- a/django-stubs/forms/forms.pyi +++ b/django-stubs/forms/forms.pyi @@ -1,28 +1,27 @@ -from collections.abc import Iterator, Mapping -from typing import Any, ClassVar +from collections.abc import Iterable, Iterator, Mapping, MutableMapping, Sequence +from typing import Any, ClassVar, overload -from django.core.exceptions import ValidationError as ValidationError -from django.core.files import uploadedfile +from django.core.exceptions import ValidationError +from django.db.models.query import _SupportsContains from django.forms.boundfield import BoundField from django.forms.fields import Field from django.forms.renderers import BaseRenderer -from django.forms.utils import ErrorDict, ErrorList, RenderableFormMixin +from django.forms.utils import ErrorDict, ErrorList, RenderableFormMixin, _DataT, _FilesT from django.forms.widgets import Media, MediaDefiningClass -from django.utils.datastructures import MultiValueDict -from django.utils.functional import _StrOrPromise -from django.utils.safestring import SafeText +from django.utils.functional import _StrOrPromise, cached_property +from typing_extensions import override class DeclarativeFieldsMetaclass(MediaDefiningClass): ... -class BaseForm(RenderableFormMixin): - default_renderer: type[BaseRenderer] - field_order: list[str] | None +class BaseForm(_SupportsContains[str], RenderableFormMixin): + default_renderer: BaseRenderer | type[BaseRenderer] | None + field_order: Iterable[str] | None use_required_attribute: bool is_bound: bool - data: dict[str, Any] - files: MultiValueDict[str, uploadedfile.UploadedFile] + data: _DataT + files: _FilesT auto_id: bool | str - initial: dict[str, Any] + initial: MutableMapping[str, Any] error_class: type[ErrorList] prefix: str | None label_suffix: str @@ -30,21 +29,28 @@ class BaseForm(RenderableFormMixin): fields: dict[str, Field] renderer: BaseRenderer cleaned_data: dict[str, Any] + template_name_div: str + template_name_p: str + template_name_table: str + template_name_ul: str + template_name_label: str + bound_field_class: type[BoundField] | None def __init__( self, - data: Mapping[str, Any] | None = ..., - files: Mapping[str, Any] | None = ..., - auto_id: bool | str | None = ..., - prefix: str | None = ..., - initial: Mapping[str, Any] | None = ..., + data: _DataT | None = None, + files: _FilesT | None = None, + auto_id: bool | str = "id_%s", + prefix: str | None = None, + initial: MutableMapping[str, Any] | None = None, error_class: type[ErrorList] = ..., - label_suffix: str | None = ..., - empty_permitted: bool = ..., - field_order: list[str] | None = ..., - use_required_attribute: bool | None = ..., - renderer: type[BaseRenderer] | None = ..., + label_suffix: str | None = None, + empty_permitted: bool = False, + field_order: Iterable[str] | None = None, + use_required_attribute: bool | None = None, + renderer: BaseRenderer | None = None, + bound_field_class: type[BoundField] | None = None, ) -> None: ... - def order_fields(self, field_order: list[str] | None) -> None: ... + def order_fields(self, field_order: Iterable[str] | None) -> None: ... def __iter__(self) -> Iterator[BoundField]: ... def __getitem__(self, name: str) -> BoundField: ... @property @@ -52,13 +58,26 @@ class BaseForm(RenderableFormMixin): def is_valid(self) -> bool: ... def add_prefix(self, field_name: str) -> str: ... def add_initial_prefix(self, field_name: str) -> str: ... + @property + def template_name(self) -> str: ... + @override + def get_context(self) -> dict[str, Any]: ... def non_field_errors(self) -> ErrorList: ... - def add_error(self, field: str | None, error: ValidationError | _StrOrPromise) -> None: ... - def has_error(self, field: str, code: str | None = ...) -> bool: ... + @overload + def add_error( + self, + field: None, + error: Mapping[str, ValidationError | _StrOrPromise | Sequence[ValidationError | _StrOrPromise]], + ) -> None: ... + @overload + def add_error( + self, field: str | None, error: ValidationError | _StrOrPromise | Sequence[ValidationError | _StrOrPromise] + ) -> None: ... + def has_error(self, field: str | None, code: str | None = None) -> bool: ... def full_clean(self) -> None: ... def clean(self) -> dict[str, Any]: ... def has_changed(self) -> bool: ... - @property + @cached_property def changed_data(self) -> list[str]: ... @property def media(self) -> Media: ... @@ -66,15 +85,9 @@ class BaseForm(RenderableFormMixin): def hidden_fields(self) -> list[BoundField]: ... def visible_fields(self) -> list[BoundField]: ... def get_initial_for_field(self, field: Field, field_name: str) -> Any: ... - def _html_output( - self, - normal_row: str, - error_row: str, - row_ender: str, - help_text_html: str, - errors_on_separate_row: bool, - ) -> SafeText: ... class Form(BaseForm, metaclass=DeclarativeFieldsMetaclass): base_fields: ClassVar[dict[str, Field]] declared_fields: ClassVar[dict[str, Field]] + +__all__ = ("BaseForm", "Form") diff --git a/django-stubs/forms/formsets.pyi b/django-stubs/forms/formsets.pyi index 4ffcc2600..3f6d14e3a 100644 --- a/django-stubs/forms/formsets.pyi +++ b/django-stubs/forms/formsets.pyi @@ -1,10 +1,14 @@ -from collections.abc import Iterator, Mapping -from typing import Any, Generic, TypeVar +from collections.abc import Iterator, Mapping, Sequence, Sized +from typing import Any, ClassVar, Generic -from django.forms import BaseForm, Form +from django.db.models.fields import _ErrorMessagesDict +from django.db.models.query import _SupportsContains +from django.forms.forms import BaseForm, Form from django.forms.renderers import BaseRenderer -from django.forms.utils import ErrorDict, ErrorList, RenderableFormMixin -from django.forms.widgets import CheckboxInput, Media, NumberInput, Widget +from django.forms.utils import ErrorList, RenderableFormMixin, _DataT, _FilesT +from django.forms.widgets import Media, MediaDefiningClass, Widget +from django.utils.functional import cached_property +from typing_extensions import TypeVar, override TOTAL_FORM_COUNT: str INITIAL_FORM_COUNT: str @@ -16,63 +20,75 @@ DELETION_FIELD_NAME: str DEFAULT_MIN_NUM: int DEFAULT_MAX_NUM: int -_BaseFormT = TypeVar("_BaseFormT", bound=BaseForm) +_F = TypeVar("_F", bound=BaseForm) -class ManagementForm(Form): ... +class ManagementForm(Form): + cleaned_data: dict[str, int | None] + @override + def clean(self) -> dict[str, int | None]: ... -class BaseFormSet(RenderableFormMixin, Generic[_BaseFormT]): - deletion_widget: type[CheckboxInput] - ordering_widget: type[NumberInput] - default_error_messages: dict[str, str] - template_name_div: str - template_name_p: str - template_name_table: str - template_name_ul: str +class BaseFormSet(_SupportsContains[_F], Sized, RenderableFormMixin, Generic[_F]): + form: type[_F] + extra: int + can_order: bool + can_delete: bool + can_delete_extra: bool + min_num: int + max_num: int + absolute_max: int + validate_min: bool + validate_max: bool is_bound: bool - prefix: str + prefix: str | None auto_id: str - data: dict[str, Any] - files: dict[str, Any] - initial: list[dict[str, Any]] | None + data: _DataT + files: _FilesT + initial: Sequence[Mapping[str, Any]] | None form_kwargs: dict[str, Any] error_class: type[ErrorList] - error_messages: dict[str, Any] - + deletion_widget: MediaDefiningClass + ordering_widget: MediaDefiningClass + default_error_messages: ClassVar[_ErrorMessagesDict] + template_name_div: str + template_name_p: str + template_name_table: str + template_name_ul: str def __init__( self, - data: Mapping[str, Any] | None = ..., - files: Mapping[str, Any] | None = ..., - auto_id: str = ..., - prefix: str | None = ..., - initial: list[dict[str, Any]] | None = ..., + data: _DataT | None = None, + files: _FilesT | None = None, + auto_id: str = "id_%s", + prefix: str | None = None, + initial: Sequence[Mapping[str, Any]] | None = None, error_class: type[ErrorList] = ..., - form_kwargs: dict[str, Any] | None = ..., - error_messages: dict[str, Any] | None = ..., + form_kwargs: dict[str, Any] | None = None, + error_messages: Mapping[str, str] | None = None, ) -> None: ... - def __iter__(self) -> Iterator[_BaseFormT]: ... - def __getitem__(self, index: int) -> _BaseFormT: ... + def __iter__(self) -> Iterator[_F]: ... + def __getitem__(self, index: int) -> _F: ... + @override def __len__(self) -> int: ... def __bool__(self) -> bool: ... - @property + @cached_property def management_form(self) -> ManagementForm: ... def total_form_count(self) -> int: ... def initial_form_count(self) -> int: ... + @cached_property + def forms(self) -> list[_F]: ... + def get_form_kwargs(self, index: int | None) -> dict[str, Any]: ... @property - def forms(self) -> list[_BaseFormT]: ... - def get_form_kwargs(self, index: int) -> dict[str, Any]: ... - @property - def initial_forms(self) -> list[_BaseFormT]: ... + def initial_forms(self) -> list[_F]: ... @property - def extra_forms(self) -> list[_BaseFormT]: ... + def extra_forms(self) -> list[_F]: ... @property - def empty_form(self) -> _BaseFormT: ... + def empty_form(self) -> _F: ... @property def cleaned_data(self) -> list[dict[str, Any]]: ... @property - def deleted_forms(self) -> list[_BaseFormT]: ... + def deleted_forms(self) -> list[_F]: ... @property - def ordered_forms(self) -> list[_BaseFormT]: ... + def ordered_forms(self) -> list[_F]: ... @classmethod def get_default_prefix(cls) -> str: ... @classmethod @@ -81,45 +97,36 @@ class BaseFormSet(RenderableFormMixin, Generic[_BaseFormT]): def get_ordering_widget(cls) -> type[Widget]: ... def non_form_errors(self) -> ErrorList: ... @property - def errors(self) -> list[ErrorDict]: ... + def errors(self) -> list[ErrorList]: ... def total_error_count(self) -> int: ... def is_valid(self) -> bool: ... def full_clean(self) -> None: ... def clean(self) -> None: ... def has_changed(self) -> bool: ... - def add_fields(self, form: _BaseFormT, index: int) -> None: ... - def add_prefix(self, index: int) -> str: ... + def add_fields(self, form: _F, index: int | None) -> None: ... + def add_prefix(self, index: int | str) -> str: ... def is_multipart(self) -> bool: ... @property def media(self) -> Media: ... + @property + def template_name(self) -> str: ... + @override def get_context(self) -> dict[str, Any]: ... -# Dynamic class produced by formset_factory -class _FormSet(BaseFormSet[_BaseFormT]): - form: type[_BaseFormT] - extra: int - can_order: bool - can_delete: bool - can_delete_extra: bool - min_num: int - max_num: int - absolute_max: int - validate_min: bool - validate_max: bool - renderer: BaseRenderer - def formset_factory( - form: type[_BaseFormT], - formset: type[BaseFormSet[_BaseFormT]] = ..., - extra: int = ..., - can_order: bool = ..., - can_delete: bool = ..., - max_num: int | None = ..., - validate_max: bool = ..., - min_num: int | None = ..., - validate_min: bool = ..., - absolute_max: int | None = ..., - can_delete_extra: bool = ..., - renderer: BaseRenderer | None = ..., -) -> type[_FormSet[_BaseFormT]]: ... -def all_valid(formsets: Iterator[BaseFormSet[Any]]) -> bool: ... + form: type[_F], + formset: type[BaseFormSet[_F]] = ..., + extra: int = 1, + can_order: bool = False, + can_delete: bool = False, + max_num: int | None = None, + validate_max: bool = False, + min_num: int | None = None, + validate_min: bool = False, + absolute_max: int | None = None, + can_delete_extra: bool = True, + renderer: BaseRenderer | None = None, +) -> type[BaseFormSet[_F]]: ... +def all_valid(formsets: Sequence[BaseFormSet[_F]]) -> bool: ... + +__all__ = ("BaseFormSet", "all_valid", "formset_factory") diff --git a/django-stubs/forms/models.pyi b/django-stubs/forms/models.pyi index 045970916..19dd0b154 100644 --- a/django-stubs/forms/models.pyi +++ b/django-stubs/forms/models.pyi @@ -1,289 +1,339 @@ -from collections.abc import Callable, Container, Iterator, Mapping, MutableMapping, Sequence -from datetime import datetime -from typing import Any, ClassVar, Literal, Protocol, TypeAlias, TypeVar -from unittest.mock import MagicMock +from collections.abc import Callable, Collection, Container, Iterator, Mapping, MutableMapping, Sequence +from typing import Any, ClassVar, Generic, Literal, TypeAlias, overload from uuid import UUID -from django.core.files.base import File from django.db import models from django.db.models import ForeignKey from django.db.models.base import Model +from django.db.models.fields import _AllLimitChoicesTo, _LimitChoicesTo from django.db.models.manager import Manager from django.db.models.query import QuerySet -from django.db.models.query_utils import Q -from django.forms.fields import CharField, ChoiceField, Field +from django.db.models.utils import AltersData +from django.forms.fields import ChoiceField, Field, _ClassLevelWidgetT from django.forms.forms import BaseForm, DeclarativeFieldsMetaclass from django.forms.formsets import BaseFormSet -from django.forms.utils import ErrorList -from django.forms.widgets import Input, Widget +from django.forms.renderers import BaseRenderer +from django.forms.utils import ErrorList, _DataT, _FilesT +from django.forms.widgets import Widget +from django.utils.choices import BaseChoiceIterator, CallableChoiceIterator, _ChoicesCallable, _ChoicesInput +from django.utils.datastructures import _PropertyDescriptor from django.utils.functional import _StrOrPromise +from typing_extensions import TypeVar, override -ALL_FIELDS: str +ALL_FIELDS: Literal["__all__"] + +_Fields: TypeAlias = Collection[str] | Literal["__all__"] +_Widgets: TypeAlias = dict[str, type[Widget] | Widget] -_Fields: TypeAlias = list[Callable[..., Any] | str] | Sequence[str] | Literal["__all__"] _Labels: TypeAlias = dict[str, str] +_HelpTexts: TypeAlias = dict[str, str] _ErrorMessages: TypeAlias = dict[str, dict[str, str]] +_FormFieldCallback: TypeAlias = Callable[[models.Field], Field | None] _M = TypeVar("_M", bound=Model) - -# Modeled from example: -# https://docs.djangoproject.com/en/4.2/topics/forms/modelforms/#overriding-the-default-fields -class FormFieldCallback(Protocol): - def __call__(self, db_field: models.Field[Any, Any], **kwargs: Any) -> Field: ... +_ParentM = TypeVar("_ParentM", bound=Model) def construct_instance( - form: BaseForm, - instance: _M, - fields: Container[str] | None = ..., - exclude: Container[str] | None = ..., + form: BaseForm, instance: _M, fields: Container[str] | None = None, exclude: Container[str] | None = None ) -> _M: ... -def model_to_dict(instance: Model, fields: _Fields | None = ..., exclude: _Fields | None = ...) -> dict[str, Any]: ... +def model_to_dict(instance: Model, fields: _Fields | None = None, exclude: _Fields | None = None) -> dict[str, Any]: ... def apply_limit_choices_to_to_formfield(formfield: Field) -> None: ... def fields_for_model( model: type[Model], - fields: _Fields | None = ..., - exclude: _Fields | None = ..., - widgets: dict[str, type[Input]] | dict[str, Widget] | None = ..., - formfield_callback: FormFieldCallback | None = ..., - localized_fields: tuple[str] | str | None = ..., - labels: _Labels | None = ..., - help_texts: dict[str, str] | None = ..., - error_messages: _ErrorMessages | None = ..., - field_classes: dict[str, type[CharField]] | None = ..., + fields: _Fields | None = None, + exclude: _Fields | None = None, + widgets: _Widgets | None = None, + formfield_callback: _FormFieldCallback | None = None, + localized_fields: _Fields | None = None, + labels: _Labels | None = None, + help_texts: _HelpTexts | None = None, + error_messages: _ErrorMessages | None = None, + field_classes: Mapping[str, type[Field]] | None = None, *, - apply_limit_choices_to: bool = ..., + apply_limit_choices_to: bool = True, + form_declared_fields: _Fields | None = None, ) -> dict[str, Any]: ... -class ModelFormOptions: - model: type[Model] | None +class ModelFormOptions(Generic[_M]): + model: type[_M] fields: _Fields | None exclude: _Fields | None - widgets: dict[str, Widget | Input] | None - localized_fields: tuple[str] | str | None + widgets: _Widgets | None + localized_fields: _Fields | None labels: _Labels | None - help_texts: dict[str, str] | None + help_texts: _HelpTexts | None error_messages: _ErrorMessages | None field_classes: dict[str, type[Field]] | None - formfield_callback: FormFieldCallback | None - def __init__(self, options: type | None = ...) -> None: ... + formfield_callback: _FormFieldCallback | None + def __init__(self, options: type | None = None) -> None: ... class ModelFormMetaclass(DeclarativeFieldsMetaclass): ... -class BaseModelForm(BaseForm): - instance: Any = ... +class BaseModelForm(BaseForm, AltersData, Generic[_M]): + instance: _M + _meta: ModelFormOptions[_M] def __init__( self, - data: Mapping[str, Any] | None = ..., - files: Mapping[str, File] | None = ..., - auto_id: bool | str = ..., - prefix: str | None = ..., - initial: dict[str, Any] | None = ..., + data: _DataT | None = None, + files: _FilesT | None = None, + auto_id: bool | str = "id_%s", + prefix: str | None = None, + initial: MutableMapping[str, Any] | None = None, error_class: type[ErrorList] = ..., - label_suffix: str | None = ..., - empty_permitted: bool = ..., - instance: Model | None = ..., - use_required_attribute: bool | None = ..., - renderer: Any = ..., + label_suffix: str | None = None, + empty_permitted: bool = False, + instance: _M | None = None, + use_required_attribute: bool | None = None, + renderer: BaseRenderer | None = None, ) -> None: ... def validate_unique(self) -> None: ... - save_m2m: Any = ... - def save(self, commit: bool = ...) -> Any: ... + def validate_constraints(self) -> None: ... + def save(self, commit: bool = True) -> _M: ... + def save_m2m(self) -> None: ... -class ModelForm(BaseModelForm, metaclass=ModelFormMetaclass): - _meta: ClassVar[ModelFormOptions] +class ModelForm(BaseModelForm[_M], metaclass=ModelFormMetaclass): + base_fields: ClassVar[dict[str, Field]] + declared_fields: ClassVar[dict[str, Field]] def modelform_factory( - model: type[Model], - form: type[ModelForm] = ..., - fields: _Fields | None = ..., - exclude: _Fields | None = ..., - formfield_callback: str | Callable[[models.Field[Any, Any]], Field] | None = ..., - widgets: MutableMapping[str, Widget] | None = ..., - localized_fields: Sequence[str] | None = ..., - labels: MutableMapping[str, str] | None = ..., - help_texts: MutableMapping[str, str] | None = ..., - error_messages: MutableMapping[str, dict[str, Any]] | None = ..., - field_classes: MutableMapping[str, type[Field]] | None = ..., -) -> type[ModelForm]: ... + model: type[_M], + form: type[ModelForm[_M]] = ..., + fields: _Fields | None = None, + exclude: _Fields | None = None, + formfield_callback: _FormFieldCallback | None = None, + widgets: _Widgets | None = None, + localized_fields: _Fields | None = None, + labels: _Labels | None = None, + help_texts: _HelpTexts | None = None, + error_messages: _ErrorMessages | None = None, + field_classes: Mapping[str, type[Field]] | None = None, +) -> type[ModelForm[_M]]: ... + +_ModelFormT = TypeVar("_ModelFormT", bound=ModelForm) -class BaseModelFormSet(BaseFormSet[ModelForm]): - model: Any = ... - unique_fields: Any = ... - queryset: Any = ... - initial_extra: Any = ... +class BaseModelFormSet(BaseFormSet[_ModelFormT], AltersData, Generic[_M, _ModelFormT]): + model: type[_M] | None + edit_only: bool + unique_fields: Collection[str] + queryset: QuerySet[_M] | None + initial_extra: Sequence[dict[str, Any]] | None def __init__( self, - data: Any | None = ..., - files: Any | None = ..., - auto_id: str = ..., - prefix: Any | None = ..., - queryset: Any | None = ..., + data: _DataT | None = None, + files: _FilesT | None = None, + auto_id: str = "id_%s", + prefix: str | None = None, + queryset: QuerySet[_M] | None = None, *, - initial: Any | None = ..., + initial: Sequence[dict[str, Any]] | None = None, **kwargs: Any, ) -> None: ... - def initial_form_count(self) -> Any: ... - def get_queryset(self) -> Any: ... - def save_new(self, form: Any, commit: bool = ...) -> Any: ... - def save_existing(self, form: Any, instance: Any, commit: bool = ...) -> Any: ... - def delete_existing(self, obj: Any, commit: bool = ...) -> None: ... - saved_forms: Any = ... - save_m2m: Any = ... - def save(self, commit: bool = ...) -> Any: ... + @override + def initial_form_count(self) -> int: ... + def get_queryset(self) -> QuerySet[_M]: ... + def save_new(self, form: _ModelFormT, commit: bool = True) -> _M: ... + def save_existing(self, form: _ModelFormT, obj: _M, commit: bool = True) -> _M: ... + def delete_existing(self, obj: _M, commit: bool = True) -> None: ... + saved_forms: list[_ModelFormT] + def save_m2m(self) -> None: ... + def save(self, commit: bool = True) -> list[_M]: ... + @override def clean(self) -> None: ... def validate_unique(self) -> None: ... - def get_unique_error_message(self, unique_check: Any) -> Any: ... - def get_date_error_message(self, date_check: Any) -> Any: ... - def get_form_error(self) -> Any: ... - changed_objects: Any = ... - deleted_objects: Any = ... - def save_existing_objects(self, commit: bool = ...) -> Any: ... - new_objects: Any = ... - def save_new_objects(self, commit: bool = ...) -> Any: ... - def add_fields(self, form: Any, index: Any) -> Any: ... + def get_unique_error_message(self, unique_check: Sequence[str]) -> str: ... + def get_date_error_message(self, date_check: tuple[str, Literal["date", "year", "month"], str, str]) -> str: ... + def get_form_error(self) -> str: ... + changed_objects: list[tuple[_M, list[str]]] + deleted_objects: list[_M] + def save_existing_objects(self, commit: bool = True) -> list[_M]: ... + new_objects: list[_M] + def save_new_objects(self, commit: bool = True) -> list[_M]: ... + @override + def add_fields(self, form: _ModelFormT, index: int | None) -> None: ... def modelformset_factory( - model: type[Model], - form: type[ModelForm] = ..., - formfield_callback: Callable[..., Any] | None = ..., + model: type[_M], + form: type[_ModelFormT] = ..., # pyright: ignore[reportInvalidTypeVarUse] + formfield_callback: _FormFieldCallback | None = None, formset: type[BaseModelFormSet] = ..., - extra: int = ..., - can_delete: bool = ..., - can_order: bool = ..., - min_num: int | None = ..., - max_num: int | None = ..., - fields: _Fields | None = ..., - exclude: _Fields | None = ..., - widgets: dict[str, Any] | None = ..., - validate_max: bool = ..., - localized_fields: Sequence[str] | None = ..., - labels: dict[str, str] | None = ..., - help_texts: dict[str, str] | None = ..., - error_messages: dict[str, dict[str, str]] | None = ..., - validate_min: bool = ..., - field_classes: dict[str, type[Field]] | None = ..., -) -> type[BaseModelFormSet]: ... + extra: int = 1, + can_delete: bool = False, + can_order: bool = False, + max_num: int | None = None, + fields: _Fields | None = None, + exclude: _Fields | None = None, + widgets: _Widgets | None = None, + validate_max: bool = False, + localized_fields: _Fields | None = None, + labels: _Labels | None = None, + help_texts: _HelpTexts | None = None, + error_messages: _ErrorMessages | None = None, + min_num: int | None = None, + validate_min: bool = False, + field_classes: Mapping[str, type[Field]] | None = None, + absolute_max: int | None = None, + can_delete_extra: bool = True, + renderer: BaseRenderer | None = None, + edit_only: bool = False, +) -> type[BaseModelFormSet[_M, _ModelFormT]]: ... -class BaseInlineFormSet(BaseModelFormSet): - instance: Any = ... - save_as_new: Any = ... - unique_fields: Any = ... +class BaseInlineFormSet(BaseModelFormSet[_M, _ModelFormT], Generic[_M, _ParentM, _ModelFormT]): + instance: _ParentM + save_as_new: bool + unique_fields: Collection[str] + fk: ForeignKey # set by inlineformset_set def __init__( self, - data: Any | None = ..., - files: Any | None = ..., - instance: Any | None = ..., - save_as_new: bool = ..., - prefix: Any | None = ..., - queryset: Any | None = ..., + data: _DataT | None = None, + files: _FilesT | None = None, + instance: _ParentM | None = None, + save_as_new: bool = False, + prefix: str | None = None, + queryset: QuerySet[_M] | None = None, **kwargs: Any, ) -> None: ... - def initial_form_count(self) -> Any: ... + @override + def initial_form_count(self) -> int: ... @classmethod - def get_default_prefix(cls) -> Any: ... - def save_new(self, form: Any, commit: bool = ...) -> Any: ... - def add_fields(self, form: Any, index: Any) -> None: ... - def get_unique_error_message(self, unique_check: Any) -> Any: ... + @override + def get_default_prefix(cls) -> str: ... + @override + def save_new(self, form: _ModelFormT, commit: bool = True) -> _M: ... + @override + def add_fields(self, form: _ModelFormT, index: int | None) -> None: ... + @override + def get_unique_error_message(self, unique_check: Sequence[str]) -> str: ... +@overload +def _get_foreign_key( + parent_model: type[Model], model: type[Model], fk_name: str | None = None, can_fail: Literal[False] = ... +) -> ForeignKey: ... +@overload +def _get_foreign_key( + parent_model: type[Model], model: type[Model], fk_name: str | None = None, can_fail: Literal[True] = ... +) -> ForeignKey | None: ... def inlineformset_factory( - parent_model: type[Model], - model: type[Model], - form: type[ModelForm] = ..., + parent_model: type[_ParentM], + model: type[_M], + form: type[_ModelFormT] = ..., # pyright: ignore[reportInvalidTypeVarUse] formset: type[BaseInlineFormSet] = ..., - fk_name: str | None = ..., - fields: _Fields | None = ..., - exclude: _Fields | None = ..., - extra: int = ..., - can_order: bool = ..., - can_delete: bool = ..., - max_num: int | None = ..., - formfield_callback: Callable[..., Any] | None = ..., - widgets: dict[str, Any] | None = ..., - validate_max: bool = ..., - localized_fields: Sequence[str] | None = ..., - labels: dict[str, str] | None = ..., - help_texts: dict[str, str] | None = ..., - error_messages: dict[str, dict[str, str]] | None = ..., - min_num: int | None = ..., - validate_min: bool = ..., - field_classes: dict[str, Any] | None = ..., -) -> type[BaseInlineFormSet]: ... + fk_name: str | None = None, + fields: _Fields | None = None, + exclude: _Fields | None = None, + extra: int = 3, + can_order: bool = False, + can_delete: bool = True, + max_num: int | None = None, + formfield_callback: _FormFieldCallback | None = None, + widgets: _Widgets | None = None, + validate_max: bool = False, + localized_fields: Sequence[str] | None = None, + labels: _Labels | None = None, + help_texts: _HelpTexts | None = None, + error_messages: _ErrorMessages | None = None, + min_num: int | None = None, + validate_min: bool = False, + field_classes: Mapping[str, type[Field]] | None = None, + absolute_max: int | None = None, + can_delete_extra: bool = True, + renderer: BaseRenderer | None = None, + edit_only: bool = False, +) -> type[BaseInlineFormSet[_M, _ParentM, _ModelFormT]]: ... class InlineForeignKeyField(Field): - disabled: bool - help_text: _StrOrPromise - required: bool - show_hidden_initial: bool - widget: Any = ... - default_error_messages: Any = ... - parent_instance: Model = ... - pk_field: bool = ... - to_field: str | None = ... + widget: _ClassLevelWidgetT + parent_instance: Model + pk_field: bool + to_field: str | None def __init__( - self, parent_instance: Model, *args: Any, pk_field: bool = ..., to_field: Any | None = ..., **kwargs: Any + self, + parent_instance: Model, + *args: Any, + pk_field: bool = False, + to_field: str | None = None, + **kwargs: Any, ) -> None: ... + @override + def clean(self, value: Any) -> Model: ... + @override + def has_changed(self, initial: Any, data: Any) -> bool: ... -class ModelChoiceIterator: - field: ModelChoiceField = ... - queryset: QuerySet[Any] | None = ... +class ModelChoiceIteratorValue: + def __init__(self, value: Any, instance: Model) -> None: ... + +class ModelChoiceIterator(BaseChoiceIterator): + field: ModelChoiceField + queryset: QuerySet def __init__(self, field: ModelChoiceField) -> None: ... - def __iter__(self) -> Iterator[tuple[int | str, str]]: ... + @override + def __iter__(self) -> Iterator[tuple[ModelChoiceIteratorValue | str, str]]: ... def __len__(self) -> int: ... def __bool__(self) -> bool: ... - def choice(self, obj: Model) -> tuple[int, str]: ... + def choice(self, obj: Model) -> tuple[ModelChoiceIteratorValue, str]: ... -class ModelChoiceField(ChoiceField): - disabled: bool - error_messages: dict[str, str] - help_text: _StrOrPromise - required: bool - show_hidden_initial: bool - validators: list[Any] - default_error_messages: Any = ... - iterator: Any = ... - empty_label: _StrOrPromise | None = ... - queryset: Any = ... - limit_choices_to: dict[str, Any] | Callable[[], Any] | None = ... - to_field_name: str | None = ... +class ModelChoiceField(ChoiceField, Generic[_M]): + iterator: type[ModelChoiceIterator] + empty_label: _StrOrPromise | None + queryset: QuerySet[_M] | None + limit_choices_to: _AllLimitChoicesTo | None + to_field_name: str | None def __init__( self, - queryset: Manager[Any] | QuerySet[Any] | None, + queryset: Manager[_M] | QuerySet[_M] | None, *, - empty_label: _StrOrPromise | None = ..., - required: bool = ..., - widget: Any | None = ..., - label: Any | None = ..., - initial: Any | None = ..., - help_text: _StrOrPromise = ..., - to_field_name: str | None = ..., - limit_choices_to: dict[str, Any] | Callable[[], Any] | None = ..., + empty_label: _StrOrPromise | None = "---------", + required: bool = True, + widget: Widget | type[Widget] | None = None, + label: _StrOrPromise | None = None, + initial: Any | None = None, + help_text: _StrOrPromise = "", + to_field_name: str | None = None, + limit_choices_to: _AllLimitChoicesTo | None = None, + blank: bool = False, **kwargs: Any, ) -> None: ... - def get_limit_choices_to( - self, - ) -> dict[str, datetime] | Q | MagicMock | None: ... - def label_from_instance(self, obj: Model) -> str: ... - choices: Any = ... - def validate(self, value: Model | None) -> None: ... - def has_changed( - self, - initial: Model | int | str | UUID | None, - data: int | str | None, - ) -> bool: ... + def validate_no_null_characters(self, value: Any) -> None: ... + def get_limit_choices_to(self) -> _LimitChoicesTo: ... + def label_from_instance(self, obj: _M) -> str: ... + choices: _PropertyDescriptor[ + _ChoicesInput | _ChoicesCallable | CallableChoiceIterator, + _ChoicesInput | CallableChoiceIterator | ModelChoiceIterator, + ] + @override + def prepare_value(self, value: Any) -> Any: ... + @override + def to_python(self, value: Any | None) -> _M | None: ... + @override + def validate(self, value: _M | None) -> None: ... + @override + def has_changed(self, initial: Model | int | str | UUID | None, data: int | str | None) -> bool: ... -class ModelMultipleChoiceField(ModelChoiceField): - disabled: bool - help_text: _StrOrPromise - required: bool - show_hidden_initial: bool - widget: Any = ... - hidden_widget: Any = ... - default_error_messages: Any = ... - def __init__(self, queryset: QuerySet[Any], **kwargs: Any) -> None: ... +class ModelMultipleChoiceField(ModelChoiceField[_M]): + widget: _ClassLevelWidgetT + hidden_widget: type[Widget] + def __init__(self, queryset: Manager[_M] | QuerySet[_M] | None, **kwargs: Any) -> None: ... + @override + def to_python(self, value: Any) -> list[_M]: ... # type: ignore[override] + @override + def clean(self, value: Any) -> QuerySet[_M]: ... + @override + def prepare_value(self, value: Any) -> Any: ... + @override + def has_changed(self, initial: Collection[Any] | None, data: Collection[Any] | None) -> bool: ... # type: ignore[override] -def _get_foreign_key( - parent_model: type[Model], - model: type[Model], - fk_name: str | None = ..., - can_fail: bool = ..., -) -> ForeignKey[Any]: ... +def modelform_defines_fields(form_class: type[ModelForm]) -> bool: ... + +__all__ = ( + "ALL_FIELDS", + "BaseInlineFormSet", + "BaseModelForm", + "BaseModelFormSet", + "ModelChoiceField", + "ModelForm", + "ModelMultipleChoiceField", + "fields_for_model", + "inlineformset_factory", + "model_to_dict", + "modelform_factory", + "modelformset_factory", +) diff --git a/django-stubs/forms/renderers.pyi b/django-stubs/forms/renderers.pyi index 16d371053..0048331f8 100644 --- a/django-stubs/forms/renderers.pyi +++ b/django-stubs/forms/renderers.pyi @@ -1,11 +1,12 @@ -from collections.abc import Mapping from typing import Any -from django.http.request import HttpRequest -from django.template.backends.base import BaseEngine, _BaseTemplate -from django.template.backends.django import Template as DjangoTemplate -from django.template.backends.jinja2 import Template as Jinja2Template -from django.utils.safestring import SafeText +from django.forms.boundfield import BoundField +from django.http import HttpRequest +from django.template.backends.base import BaseEngine +from django.template.backends.django import DjangoTemplates as DjangoTemplatesR +from django.template.backends.jinja2 import Jinja2 as Jinja2R +from django.template.base import Template +from django.utils.functional import cached_property from typing_extensions import override def get_default_renderer() -> BaseRenderer: ... @@ -13,28 +14,23 @@ def get_default_renderer() -> BaseRenderer: ... class BaseRenderer: form_template_name: str formset_template_name: str - def get_template(self, template_name: str) -> _BaseTemplate: ... - def render( - self, - template_name: str, - context: Mapping[str, Any], - request: HttpRequest | None = ..., - ) -> SafeText: ... + field_template_name: str + bound_field_class: type[BoundField] | None + def get_template(self, template_name: str) -> Any: ... + def render(self, template_name: str, context: dict[str, Any], request: HttpRequest | None = None) -> str: ... class EngineMixin: - backend: BaseEngine - def get_template(self, template_name: str) -> _BaseTemplate: ... - @property + def get_template(self, template_name: str) -> Any: ... + @cached_property def engine(self) -> BaseEngine: ... class DjangoTemplates(EngineMixin, BaseRenderer): - @override - def get_template(self, template_name: str) -> DjangoTemplate: ... + backend: type[DjangoTemplatesR] class Jinja2(EngineMixin, BaseRenderer): - @override - def get_template(self, template_name: str) -> Jinja2Template: ... + @cached_property + def backend(self) -> type[Jinja2R]: ... -class DjangoDivFormRenderer(DjangoTemplates): ... -class Jinja2DivFormRenderer(Jinja2): ... -class TemplatesSetting(BaseRenderer): ... +class TemplatesSetting(BaseRenderer): + @override + def get_template(self, template_name: str) -> Template | None: ... diff --git a/django-stubs/forms/utils.pyi b/django-stubs/forms/utils.pyi index a70d3d788..626a44082 100644 --- a/django-stubs/forms/utils.pyi +++ b/django-stubs/forms/utils.pyi @@ -1,60 +1,84 @@ from collections import UserList from collections.abc import Mapping, Sequence from datetime import datetime -from typing import Any +from typing import Any, TypeAlias from django.core.exceptions import ValidationError +from django.core.files.uploadedfile import UploadedFile from django.forms.renderers import BaseRenderer +from django.utils.datastructures import MultiValueDict from django.utils.functional import _StrOrPromise -from django.utils.safestring import SafeText +from django.utils.safestring import SafeString +from typing_extensions import override + +_DataT: TypeAlias = Mapping[str, Any] # noqa: PYI047 + +_FilesT: TypeAlias = MultiValueDict[str, UploadedFile] # noqa: PYI047 def pretty_name(name: str) -> str: ... -def flatatt(attrs: dict[str, Any]) -> SafeText: ... +def flatatt(attrs: dict[str, Any]) -> SafeString: ... class RenderableMixin: - def get_context(self) -> Mapping[str, Any]: ... + def get_context(self) -> dict[str, Any]: ... def render( self, - template_name: str | None = ..., - context: Mapping[str, Any] | None = ..., - renderer: BaseRenderer | None = ..., - ) -> SafeText: ... + template_name: str | None = None, + context: dict[str, Any] | None = None, + renderer: BaseRenderer | type[BaseRenderer] | None = None, + ) -> SafeString: ... + # This is a lie, but this is how it is supposed to be used, + # in reallity it is `__str__ = __html__ = render`: + @override + def __str__(self) -> SafeString: ... + def __html__(self) -> SafeString: ... + +class RenderableFieldMixin(RenderableMixin): + def as_field_group(self) -> SafeString: ... + def as_hidden(self) -> SafeString: ... + def as_widget(self) -> SafeString: ... class RenderableFormMixin(RenderableMixin): - def as_p(self) -> SafeText: ... - def as_table(self) -> SafeText: ... - def as_ul(self) -> SafeText: ... - def as_div(self) -> SafeText: ... + def as_p(self) -> SafeString: ... + def as_table(self) -> SafeString: ... + def as_ul(self) -> SafeString: ... + def as_div(self) -> SafeString: ... class RenderableErrorMixin(RenderableMixin): - def as_json(self, escape_html: bool = ...) -> str: ... - def as_text(self) -> SafeText: ... - def as_ul(self) -> SafeText: ... + def as_json(self, escape_html: bool = False) -> str: ... + def as_text(self) -> SafeString: ... + def as_ul(self) -> SafeString: ... class ErrorDict(dict[str, ErrorList], RenderableErrorMixin): template_name: str template_name_text: str template_name_ul: str renderer: BaseRenderer - def __init__(self, *args: Any, renderer: BaseRenderer | None = ..., **kwargs: Any): ... + + def __init__(self, *args: Any, renderer: BaseRenderer | None = None, **kwargs: Any): ... def as_data(self) -> dict[str, list[ValidationError]]: ... - def get_json_data(self, escape_html: bool = ...) -> dict[str, Any]: ... + def get_json_data(self, escape_html: bool = False) -> dict[str, Any]: ... + @override + def get_context(self) -> dict[str, Any]: ... class ErrorList(UserList[ValidationError | _StrOrPromise], RenderableErrorMixin): template_name: str template_name_text: str template_name_ul: str - data: list[ValidationError | _StrOrPromise] error_class: str renderer: BaseRenderer def __init__( self, - initlist: Sequence[str | Exception] | None = ..., - error_class: str | None = ..., + initlist: ErrorList | Sequence[str | Exception] | None = None, + error_class: str | None = None, renderer: BaseRenderer | None = None, + field_id: str | None = None, ) -> None: ... def as_data(self) -> list[ValidationError]: ... - def get_json_data(self, escape_html: bool = ...) -> list[dict[str, str]]: ... + @override + def copy(self) -> ErrorList: ... + def get_json_data(self, escape_html: bool = False) -> list[dict[str, str]]: ... + @override + def get_context(self) -> dict[str, Any]: ... def from_current_timezone(value: datetime) -> datetime: ... def to_current_timezone(value: datetime) -> datetime: ... diff --git a/django-stubs/forms/widgets.pyi b/django-stubs/forms/widgets.pyi index c7a1f17ed..a12afcacf 100644 --- a/django-stubs/forms/widgets.pyi +++ b/django-stubs/forms/widgets.pyi @@ -1,187 +1,409 @@ -from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence -from decimal import Decimal -from itertools import chain -from typing import Any, TypeAlias +import datetime +from collections.abc import Iterable, Iterator, Mapping, Sequence +from typing import Any, Literal, Protocol, TypeAlias, type_check_only +import _typeshed from django.core.files.base import File from django.forms.renderers import BaseRenderer -from django.utils.safestring import SafeText +from django.forms.utils import _DataT, _FilesT +from django.utils.choices import _Choices +from django.utils.datastructures import _ListOrTuple +from django.utils.safestring import SafeString +from typing_extensions import Self, override _OptAttrs: TypeAlias = dict[str, Any] class MediaOrderConflictWarning(RuntimeWarning): ... +class MediaAsset: + element_template: str + + def __init__(self, path: str, **attributes: Any) -> None: ... + @override + def __eq__(self, other: object) -> bool: ... + @override + def __hash__(self) -> int: ... + def __html__(self) -> SafeString: ... + @property + def path(self) -> str: ... + +class Script(MediaAsset): + element_template: str + + def __init__(self, src: str, **attributes: Any) -> None: ... + class Media: - _js: str def __init__( self, - media: type | None = ..., - css: dict[str, Iterable[str]] | None = ..., - js: Iterable[str] | None = ..., + media: type | None = None, + css: dict[str, Sequence[str]] | None = None, + js: Sequence[str] | None = None, ) -> None: ... - def render(self) -> str: ... - def render_js(self) -> list[str]: ... - def render_css(self) -> chain[Any]: ... + def render(self) -> SafeString: ... + def render_js(self) -> list[SafeString]: ... + def render_css(self) -> Iterable[SafeString]: ... def absolute_path(self, path: str) -> str: ... def __getitem__(self, name: str) -> Media: ... @staticmethod - def merge(list_1: Iterable[Any], list_2: Iterable[Any]) -> Iterable[Any]: ... + def merge(*lists: Iterable[Any]) -> list[Any]: ... def __add__(self, other: Media) -> Media: ... + def __html__(self) -> SafeString: ... -class MediaDefiningClass(type): ... +class MediaDefiningClass(type): + def __new__( + mcs: type[_typeshed.Self], # noqa: TID251 + name: str, + bases: tuple[type, ...], + attrs: dict[str, Any], + ) -> _typeshed.Self: ... # noqa: TID251 -class Widget: - needs_multipart_form: bool = ... - is_localized: bool = ... - is_required: bool = ... - supports_microseconds: bool = ... - attrs: _OptAttrs = ... - def __init__(self, attrs: _OptAttrs | None = ...) -> None: ... +class Widget(metaclass=MediaDefiningClass): + needs_multipart_form: bool + is_localized: bool + is_required: bool + supports_microseconds: bool + attrs: _OptAttrs + template_name: str + use_fieldset: bool + def __init__(self, attrs: _OptAttrs | None = None) -> None: ... + def __deepcopy__(self, memo: dict[int, Any]) -> Self: ... @property def is_hidden(self) -> bool: ... - def subwidgets(self, name: str, value: list[str] | None, attrs: _OptAttrs = ...) -> Iterator[dict[str, Any]]: ... + @property + def media(self) -> Media: ... + def subwidgets(self, name: str, value: Any, attrs: _OptAttrs | None = None) -> Iterator[dict[str, Any]]: ... def format_value(self, value: Any) -> str | None: ... def get_context(self, name: str, value: Any, attrs: _OptAttrs | None) -> dict[str, Any]: ... def render( - self, - name: str, - value: Any, - attrs: _OptAttrs | None = ..., - renderer: BaseRenderer | None = ..., - ) -> SafeText: ... - def build_attrs( - self, base_attrs: _OptAttrs, extra_attrs: _OptAttrs | None = ... - ) -> dict[str, Decimal | float | str]: ... - def value_from_datadict(self, data: dict[str, Any], files: Mapping[str, Iterable[Any]], name: str) -> Any: ... - def value_omitted_from_data(self, data: dict[str, Any], files: Mapping[str, Iterable[Any]], name: str) -> bool: ... + self, name: str, value: Any, attrs: _OptAttrs | None = None, renderer: BaseRenderer | None = None + ) -> SafeString: ... + def build_attrs(self, base_attrs: _OptAttrs, extra_attrs: _OptAttrs | None = None) -> dict[str, Any]: ... + def value_from_datadict(self, data: _DataT, files: _FilesT, name: str) -> Any: ... + def value_omitted_from_data(self, data: _DataT, files: _FilesT, name: str) -> bool: ... def id_for_label(self, id_: str) -> str: ... def use_required_attribute(self, initial: Any) -> bool: ... class Input(Widget): - input_type: str = ... - template_name: str = ... + input_type: str + template_name: str + @override + def get_context(self, name: str, value: Any, attrs: _OptAttrs | None) -> dict[str, Any]: ... + +class TextInput(Input): + input_type: str + template_name: str -class TextInput(Input): ... -class NumberInput(Input): ... -class EmailInput(Input): ... -class URLInput(Input): ... +class NumberInput(Input): + input_type: str + template_name: str + +class EmailInput(Input): + input_type: str + template_name: str + +class URLInput(Input): + input_type: str + template_name: str + +class ColorInput(Input): + input_type: str + template_name: str + +class SearchInput(Input): + input_type: str + template_name: str + +class TelInput(Input): + input_type: str + template_name: str class PasswordInput(Input): - render_value: bool = ... - def __init__(self, attrs: _OptAttrs | None = ..., render_value: bool = ...) -> None: ... + render_value: bool + input_type: str + template_name: str + def __init__(self, attrs: _OptAttrs | None = None, render_value: bool = False) -> None: ... + @override + def get_context(self, name: str, value: Any, attrs: _OptAttrs | None) -> dict[str, Any]: ... class HiddenInput(Input): - choices: Iterable[tuple[str, str]] + input_type: str + template_name: str -class MultipleHiddenInput(HiddenInput): ... -class FileInput(Input): ... +class MultipleHiddenInput(HiddenInput): + template_name: str + @override + def get_context(self, name: str, value: Any, attrs: _OptAttrs | None) -> dict[str, Any]: ... + @override + def value_from_datadict(self, data: _DataT, files: _FilesT, name: str) -> Any: ... + @override + def format_value(self, value: Any) -> list[str]: ... # type: ignore[override] + +class FileInput(Input): + allow_multiple_selected: bool + input_type: str + template_name: str + needs_multipart_form: bool + @override + def format_value(self, value: Any) -> None: ... + @override + def value_from_datadict(self, data: _DataT, files: _FilesT, name: str) -> Any: ... + @override + def value_omitted_from_data(self, data: _DataT, files: _FilesT, name: str) -> bool: ... + @override + def use_required_attribute(self, initial: Any) -> bool: ... -FILE_INPUT_CONTRADICTION: Any +FILE_INPUT_CONTRADICTION: object class ClearableFileInput(FileInput): - clear_checkbox_label: Any = ... - initial_text: Any = ... - input_text: Any = ... + clear_checkbox_label: str + initial_text: str + input_text: str + template_name: str + checked: bool def clear_checkbox_name(self, name: str) -> str: ... def clear_checkbox_id(self, name: str) -> str: ... def is_initial(self, value: File | str | None) -> bool: ... + @override + def format_value(self, value: Any) -> Any: ... + @override + def get_context(self, name: str, value: Any, attrs: _OptAttrs | None) -> dict[str, Any]: ... + @override + def value_from_datadict(self, data: _DataT, files: _FilesT, name: str) -> Any: ... + @override + def value_omitted_from_data(self, data: _DataT, files: _FilesT, name: str) -> bool: ... class Textarea(Widget): - template_name: str = ... + template_name: str + def __init__(self, attrs: _OptAttrs | None = None) -> None: ... class DateTimeBaseInput(TextInput): - format_key: str = ... - format: str | None = ... - def __init__(self, attrs: _OptAttrs | None = ..., format: str | None = ...) -> None: ... + format_key: str + format: str | None + supports_microseconds: bool + def __init__(self, attrs: _OptAttrs | None = None, format: str | None = None) -> None: ... + @override + def format_value(self, value: Any) -> str | None: ... + +class DateInput(DateTimeBaseInput): + format_key: str + template_name: str -class DateInput(DateTimeBaseInput): ... -class DateTimeInput(DateTimeBaseInput): ... -class TimeInput(DateTimeBaseInput): ... +class DateTimeInput(DateTimeBaseInput): + format_key: str + template_name: str + +class TimeInput(DateTimeBaseInput): + format_key: str + template_name: str + +def boolean_check(v: Any) -> bool: ... + +@type_check_only +class _CheckCallable(Protocol): + def __call__(self, value: Any, /) -> bool: ... class CheckboxInput(Input): - check_test: Callable[..., Any] = ... - def __init__( - self, - attrs: _OptAttrs | None = ..., - check_test: Callable[..., Any] | None = ..., - ) -> None: ... + check_test: _CheckCallable + input_type: str + template_name: str + def __init__(self, attrs: _OptAttrs | None = None, check_test: _CheckCallable | None = None) -> None: ... + @override + def format_value(self, value: Any) -> str | None: ... + @override + def get_context(self, name: str, value: Any, attrs: _OptAttrs | None) -> dict[str, Any]: ... + @override + def value_from_datadict(self, data: _DataT, files: _FilesT, name: str) -> bool: ... + @override + def value_omitted_from_data(self, data: _DataT, files: _FilesT, name: str) -> bool: ... class ChoiceWidget(Widget): - allow_multiple_selected: bool = ... - input_type: str | None = ... - template_name: str | None = ... - option_template_name: str = ... - add_id_index: bool = ... - checked_attribute: Any = ... - option_inherits_attrs: bool = ... - choices: list[list[int | str]] = ... - def __init__(self, attrs: _OptAttrs | None = ..., choices: Sequence[tuple[Any, Any]] = ...) -> None: ... - def options(self, name: str, value: list[str], attrs: _OptAttrs | None = ...) -> None: ... - def optgroups(self, name: str, value: list[str], attrs: _OptAttrs | None = ...) -> Any: ... + allow_multiple_selected: bool + input_type: str + template_name: str + option_template_name: str + add_id_index: bool + checked_attribute: Any + option_inherits_attrs: bool + choices: _Choices + def __init__(self, attrs: _OptAttrs | None = None, choices: _Choices = ()) -> None: ... + @override + def subwidgets(self, name: str, value: Any, attrs: _OptAttrs | None = None) -> Iterator[dict[str, Any]]: ... + def options(self, name: str, value: list[str], attrs: _OptAttrs | None = None) -> Iterator[dict[str, Any]]: ... + def optgroups( + self, name: str, value: list[str], attrs: _OptAttrs | None = None + ) -> list[tuple[str | None, list[dict[str, Any]], int | None]]: ... def create_option( self, name: str, value: Any, label: int | str, - selected: set[str] | bool, + selected: bool, index: int, - subindex: int | None = ..., - attrs: _OptAttrs | None = ..., + subindex: int | None = None, + attrs: _OptAttrs | None = None, ) -> dict[str, Any]: ... - def id_for_label(self, id_: str, index: str = ...) -> str: ... + @override + def get_context(self, name: str, value: Any, attrs: _OptAttrs | None) -> dict[str, Any]: ... + @override + def id_for_label(self, id_: str, index: str = "0") -> str: ... + @override + def value_from_datadict(self, data: _DataT, files: _FilesT, name: str) -> Any: ... + @override + def format_value(self, value: Any) -> list[str]: ... # type: ignore[override] + +class Select(ChoiceWidget): + input_type: str + template_name: str + option_template_name: str + add_id_index: bool + checked_attribute: Any + option_inherits_attrs: bool + @override + def get_context(self, name: str, value: Any, attrs: _OptAttrs | None) -> dict[str, Any]: ... + @override + def use_required_attribute(self, initial: Any) -> bool: ... -class Select(ChoiceWidget): ... -class NullBooleanSelect(Select): ... +class NullBooleanSelect(Select): + def __init__(self, attrs: _OptAttrs | None = None) -> None: ... + @override + def format_value(self, value: Any) -> str: ... # type: ignore[override] + @override + def value_from_datadict(self, data: _DataT, files: _FilesT, name: str) -> bool | None: ... class SelectMultiple(Select): - allow_multiple_selected: bool = ... + allow_multiple_selected: bool + @override + def value_from_datadict(self, data: _DataT, files: _FilesT, name: str) -> Any: ... + @override + def value_omitted_from_data(self, data: _DataT, files: _FilesT, name: str) -> bool: ... class RadioSelect(ChoiceWidget): - can_add_related: bool + input_type: str + template_name: str + option_template_name: str + @override + def id_for_label(self, id_: str, index: str | None = None) -> str: ... -class CheckboxSelectMultiple(ChoiceWidget): ... +class CheckboxSelectMultiple(RadioSelect): + input_type: str + template_name: str + option_template_name: str + @override + def use_required_attribute(self, initial: Any) -> bool: ... + @override + def value_omitted_from_data(self, data: _DataT, files: _FilesT, name: str) -> bool: ... class MultiWidget(Widget): - template_name: str = ... - widgets: list[Widget] = ... + template_name: str + widgets: Sequence[Widget] def __init__( self, - widgets: Sequence[Widget | type[Widget]], - attrs: _OptAttrs | None = ..., + widgets: dict[str, Widget | type[Widget]] | Sequence[Widget | type[Widget]], + attrs: _OptAttrs | None = None, ) -> None: ... + @property + @override + def is_hidden(self) -> bool: ... + @override + def get_context(self, name: str, value: Any, attrs: _OptAttrs | None) -> dict[str, Any]: ... + @override + def id_for_label(self, id_: str) -> str: ... + @override + def value_from_datadict(self, data: _DataT, files: _FilesT, name: str) -> list[Any]: ... + @override + def value_omitted_from_data(self, data: _DataT, files: _FilesT, name: str) -> bool: ... def decompress(self, value: Any) -> Any | None: ... - media: Any = ... + @property + @override + def needs_multipart_form(self) -> bool: ... # type: ignore[override] class SplitDateTimeWidget(MultiWidget): + supports_microseconds: bool + template_name: str + widgets: tuple[DateInput, TimeInput] # pyright: ignore[reportIncompatibleVariableOverride] def __init__( self, - attrs: _OptAttrs | None = ..., - date_format: str | None = ..., - time_format: str | None = ..., - date_attrs: dict[str, str] | None = ..., - time_attrs: dict[str, str] | None = ..., + attrs: _OptAttrs | None = None, + date_format: str | None = None, + time_format: str | None = None, + date_attrs: dict[str, str] | None = None, + time_attrs: dict[str, str] | None = None, ) -> None: ... + @override + def decompress(self, value: Any) -> tuple[datetime.date | None, datetime.time | None]: ... -class SplitHiddenDateTimeWidget(SplitDateTimeWidget): ... +class SplitHiddenDateTimeWidget(SplitDateTimeWidget): + template_name: str + def __init__( + self, + attrs: _OptAttrs | None = None, + date_format: str | None = None, + time_format: str | None = None, + date_attrs: dict[str, str] | None = None, + time_attrs: dict[str, str] | None = None, + ) -> None: ... class SelectDateWidget(Widget): - none_value: Any = ... - month_field: str = ... - day_field: str = ... - year_field: str = ... - template_name: str = ... - input_type: str = ... - select_widget: Any = ... - date_re: Any = ... - years: Any = ... - months: Any = ... - year_none_value: Any = ... - month_none_value: Any = ... - day_none_value: Any = ... + none_value: tuple[Literal[""], str] + month_field: str + day_field: str + year_field: str + template_name: str + input_type: str + select_widget: type[ChoiceWidget] + date_re: Any + years: Iterable[int | str] + months: Mapping[int, str] + year_none_value: tuple[Literal[""], str] + month_none_value: tuple[Literal[""], str] + day_none_value: tuple[Literal[""], str] def __init__( self, - attrs: _OptAttrs | None = ..., - years: Iterable[int | str] | None = ..., - months: dict[int, str] | None = ..., - empty_label: str | Sequence[str] | None = ..., + attrs: _OptAttrs | None = None, + years: Iterable[int | str] | None = None, + months: Mapping[int, str] | None = None, + empty_label: str | _ListOrTuple[str] | None = None, ) -> None: ... + @override + def get_context(self, name: str, value: Any, attrs: _OptAttrs | None) -> dict[str, Any]: ... + @override + def format_value(self, value: Any) -> dict[str, str | int | None]: ... # type: ignore[override] + @override + def id_for_label(self, id_: str) -> str: ... + @override + def value_from_datadict(self, data: _DataT, files: _FilesT, name: str) -> str | None | Any: ... + @override + def value_omitted_from_data(self, data: _DataT, files: _FilesT, name: str) -> bool: ... + +__all__ = ( + "CheckboxInput", + "CheckboxSelectMultiple", + "ClearableFileInput", + "ColorInput", + "DateInput", + "DateTimeInput", + "EmailInput", + "FileInput", + "HiddenInput", + "Media", + "MediaDefiningClass", + "MultiWidget", + "MultipleHiddenInput", + "NullBooleanSelect", + "NumberInput", + "PasswordInput", + "RadioSelect", + "Script", + "SearchInput", + "Select", + "SelectDateWidget", + "SelectMultiple", + "SplitDateTimeWidget", + "SplitHiddenDateTimeWidget", + "TelInput", + "TextInput", + "Textarea", + "TimeInput", + "URLInput", + "Widget", +) diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index cba473816..743cbaa09 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -126,7 +126,7 @@ class QueryDict(MultiValueDict[str, str]): def appendlist(self, key: str, value: str) -> None: ... def urlencode(self, safe: str | None = ...) -> str: ... @classmethod - def fromkeys( + def fromkeys( # type: ignore[override] cls, iterable: Iterable[bytes | str], value: str | bytes = ..., diff --git a/django-stubs/template/backends/base.pyi b/django-stubs/template/backends/base.pyi index 95dd07b04..1555f4238 100644 --- a/django-stubs/template/backends/base.pyi +++ b/django-stubs/template/backends/base.pyi @@ -1,26 +1,28 @@ from collections.abc import Iterator, Mapping -from typing import Any, Protocol +from typing import Any, Protocol, type_check_only +from django.core.checks.messages import CheckMessage from django.http.request import HttpRequest -from django.utils.safestring import SafeText - -# Source: https://github.com/django/deps/blob/main/final/0182-multiple-template-engines.rst#backends-api -class _BaseTemplate(Protocol): - def render( - self, - context: Mapping[str, Any] | None = ..., - request: HttpRequest | None = ..., - ) -> SafeText | str: ... +from django.utils.functional import cached_property class BaseEngine: - name: str = ... - dirs: list[str] = ... - app_dirs: bool = ... + name: str + dirs: list[str] + app_dirs: bool def __init__(self, params: Mapping[str, Any]) -> None: ... + def check(self, **kwargs: Any) -> list[CheckMessage]: ... @property def app_dirname(self) -> str: ... - def from_string(self, template_code: str) -> _BaseTemplate: ... - def get_template(self, template_name: str) -> _BaseTemplate: ... - @property - def template_dirs(self) -> tuple[str]: ... + def from_string(self, template_code: str) -> _EngineTemplate: ... + def get_template(self, template_name: str) -> _EngineTemplate: ... + @cached_property + def template_dirs(self) -> tuple[str, ...]: ... def iter_template_filenames(self, template_name: str) -> Iterator[str]: ... + +@type_check_only +class _EngineTemplate(Protocol): + def render( + self, + context: dict[str, Any] | None = ..., + request: HttpRequest | None = ..., + ) -> str: ... diff --git a/django-stubs/template/context.pyi b/django-stubs/template/context.pyi index f309e1c7d..79393e22e 100644 --- a/django-stubs/template/context.pyi +++ b/django-stubs/template/context.pyi @@ -1,92 +1,91 @@ from collections.abc import Callable, Iterable, Iterator +from contextlib import AbstractContextManager from types import TracebackType from typing import Any, TypeAlias from django.http.request import HttpRequest from django.template.base import Node, Origin, Template -from django.template.defaulttags import IfChangedNode from django.template.loader_tags import IncludeNode -from typing_extensions import Self +from typing_extensions import Self, override + +_ContextKeys: TypeAlias = int | str | Node _ContextValues: TypeAlias = dict[str, Any] | Context class ContextPopException(Exception): ... -class ContextDict(dict[Any, Any]): - context: BaseContext = ... +class ContextDict(dict): + context: BaseContext def __init__(self, context: BaseContext, *args: Any, **kwargs: Any) -> None: ... def __enter__(self) -> Self: ... def __exit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, - traceback: TracebackType | None, + exc_tb: TracebackType | None, ) -> None: ... class BaseContext(Iterable[Any]): - def __init__(self, dict_: Any = ...) -> None: ... - def __copy__(self) -> BaseContext: ... + def __init__(self, dict_: Any | None = None) -> None: ... + def __copy__(self) -> Self: ... + @override def __iter__(self) -> Iterator[Any]: ... def push(self, *args: Any, **kwargs: Any) -> ContextDict: ... def pop(self) -> ContextDict: ... - def __setitem__(self, key: Node | str, value: Any) -> None: ... - def set_upward(self, key: str, value: int | str) -> None: ... - def __getitem__(self, key: int | str) -> Any: ... - def __delitem__(self, key: Any) -> None: ... - def __contains__(self, key: str) -> bool: ... - def get(self, key: str, otherwise: Any | None = ...) -> Any | None: ... - def setdefault( - self, - key: IfChangedNode | str, - default: list[Origin] | int | None = ..., - ) -> list[Origin] | int | None: ... - def new(self, values: _ContextValues | None = ...) -> Context: ... - def flatten( - self, - ) -> dict[str, dict[str, type[Any] | str] | int | str | None]: ... + def __setitem__(self, key: _ContextKeys, value: Any) -> None: ... + def set_upward(self, key: _ContextKeys, value: int | str) -> None: ... + def __getitem__(self, key: _ContextKeys) -> Any: ... + def __delitem__(self, key: _ContextKeys) -> None: ... + def __contains__(self, key: _ContextKeys) -> bool: ... + def get(self, key: _ContextKeys, otherwise: Any | None = None) -> Any | None: ... + def setdefault(self, key: _ContextKeys, default: list[Origin] | int | None = None) -> list[Origin] | int | None: ... + def new(self, values: _ContextValues | None = None) -> Context: ... + def flatten(self) -> dict[_ContextKeys, dict[_ContextKeys, type[Any] | str] | int | str | None]: ... class Context(BaseContext): dicts: Any - autoescape: bool = ... - use_l10n: bool | None = ... - use_tz: bool | None = ... - template_name: str | None = ... - render_context: RenderContext = ... - template: Template | None = ... + autoescape: bool + use_l10n: bool | None + use_tz: bool | None + template_name: str | None + render_context: RenderContext + template: Template | None def __init__( self, - dict_: Any = ..., - autoescape: bool = ..., - use_l10n: bool | None = ..., - use_tz: None = ..., + dict_: Any | None = None, + autoescape: bool = True, + use_l10n: bool | None = None, + use_tz: bool | None = None, ) -> None: ... - def bind_template(self, template: Template) -> Iterator[None]: ... + def bind_template(self, template: Template) -> AbstractContextManager[None]: ... def update(self, other_dict: dict[str, Any] | Context) -> ContextDict: ... class RenderContext(BaseContext): dicts: list[dict[IncludeNode | str, str]] - template: Template | None = ... - def push_state(self, template: Template, isolated_context: bool = ...) -> Iterator[None]: ... + template: Template | None + def push_state(self, template: Template, isolated_context: bool = True) -> AbstractContextManager[None]: ... class RequestContext(Context): autoescape: bool dicts: list[dict[str, str]] render_context: RenderContext template_name: str | None - use_l10n: None # pyright: ignore[reportIncompatibleVariableOverride] - use_tz: None # pyright: ignore[reportIncompatibleVariableOverride] - request: HttpRequest = ... + use_l10n: bool | None + use_tz: bool | None + request: HttpRequest def __init__( self, request: HttpRequest, - dict_: dict[str, Any] | None = ..., - processors: list[Callable[..., Any]] | None = ..., - use_l10n: None = ..., - use_tz: None = ..., - autoescape: bool = ..., + dict_: dict[str, Any] | None = None, + processors: list[Callable] | None = None, + use_l10n: bool | None = None, + use_tz: bool | None = None, + autoescape: bool = True, ) -> None: ... - template: Template | None = ... - def bind_template(self, template: Template) -> Iterator[None]: ... - def new(self, values: _ContextValues | None = ...) -> RequestContext: ... + template: Template | None + @override + def bind_template(self, template: Template) -> AbstractContextManager[None]: ... + @override + def new(self, values: _ContextValues | None = None) -> RequestContext: ... -def make_context(context: Any, request: HttpRequest | None = ..., **kwargs: Any) -> Context: ... +def make_context(context: dict[str, Any] | None, request: HttpRequest | None = None, **kwargs: Any) -> Context: ... diff --git a/django-stubs/template/exceptions.pyi b/django-stubs/template/exceptions.pyi index 8478feef3..57b33421a 100644 --- a/django-stubs/template/exceptions.pyi +++ b/django-stubs/template/exceptions.pyi @@ -2,15 +2,15 @@ from django.template.backends.base import BaseEngine from django.template.base import Origin class TemplateDoesNotExist(Exception): - backend: BaseEngine | None = ... - tried: list[tuple[Origin, str]] = ... - chain: list[TemplateDoesNotExist] = ... + backend: BaseEngine | None + tried: list[tuple[Origin, str]] + chain: list[TemplateDoesNotExist] def __init__( self, msg: Origin | str, - tried: list[tuple[Origin, str]] | None = ..., - backend: BaseEngine | None = ..., - chain: list[TemplateDoesNotExist] | None = ..., + tried: list[tuple[Origin, str]] | None = None, + backend: BaseEngine | None = None, + chain: list[TemplateDoesNotExist] | None = None, ) -> None: ... class TemplateSyntaxError(Exception): ... diff --git a/django-stubs/template/library.pyi b/django-stubs/template/library.pyi index 115ac3626..b95cca73d 100644 --- a/django-stubs/template/library.pyi +++ b/django-stubs/template/library.pyi @@ -1,116 +1,103 @@ -from collections.abc import Callable -from typing import Any, TypeVar, overload +from collections.abc import Callable, Collection, Iterable, Mapping, Sequence, Sized +from typing import Any, overload -from django.template.base import FilterExpression, Origin, Parser, Token +from django.template.base import FilterExpression, Parser from django.template.context import Context -from django.utils.safestring import SafeText -from typing_extensions import ParamSpec +from django.utils.safestring import SafeString +from typing_extensions import TypeVar -from .base import Node, Template - -_T = TypeVar("_T") -_P = ParamSpec("_P") +from .base import Node, NodeList, Template class InvalidTemplateLibrary(Exception): ... +_C = TypeVar("_C", bound=Callable[..., Any]) + class Library: - filters: dict[str, Callable[..., Any]] = ... - tags: dict[str, Callable[..., Any]] = ... + filters: dict[str, Callable[..., Any]] + tags: dict[str, Callable[..., Any]] def __init__(self) -> None: ... - - # Both arguments None @overload - def tag( - self, - name: None = ..., - compile_function: None = ..., - ) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]: ... - # Only name as function - @overload - def tag( - self, - name: Callable[_P, _T], - compile_function: None = ..., - ) -> Callable[_P, _T]: ... - # Only name as string + def tag(self, name: _C) -> _C: ... @overload - def tag( - self, - name: str, - compile_function: None = ..., - ) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]: ... - # Both arguments specified + def tag(self, name: str, compile_function: _C) -> _C: ... @overload - def tag( - self, - name: str, - compile_function: Callable[_P, _T], - ) -> Callable[_P, _T]: ... - def tag_function(self, func: Callable[_P, _T]) -> Callable[_P, _T]: ... - - # Both arguments None + def tag(self, name: str | None = None, compile_function: None = None) -> Callable[[_C], _C]: ... + def tag_function(self, func: _C) -> _C: ... @overload def filter( self, - name: None = ..., - filter_func: None = ..., - **flags: Any, - ) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]: ... - # Only name as string + name: _C, + filter_func: None = None, + *, + is_safe: bool = False, + needs_autoescape: bool = False, + expects_localtime: bool = False, + ) -> _C: ... @overload def filter( self, - name: str = ..., - filter_func: None = ..., - **flags: Any, - ) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]: ... - # Only name as callable + name: str | None, + filter_func: _C, + *, + is_safe: bool = False, + needs_autoescape: bool = False, + expects_localtime: bool = False, + ) -> _C: ... @overload def filter( self, - name: Callable[_P, _T], - filter_func: None = ..., - **flags: Any, - ) -> Callable[_P, _T]: ... - # Both arguments + name: str | None = None, + filter_func: None = None, + *, + is_safe: bool = False, + needs_autoescape: bool = False, + expects_localtime: bool = False, + ) -> Callable[[_C], _C]: ... + def filter_function(self, func: _C, **flags: Any) -> _C: ... @overload - def filter( - self, - name: str, - filter_func: Callable[_P, _T], - **flags: Any, - ) -> Callable[_P, _T]: ... - def filter_function(self, func: Callable[_P, _T], **flags: Any) -> Callable[_P, _T]: ... - - # func is None + def simple_tag(self, func: _C, takes_context: bool | None = None, name: str | None = None) -> _C: ... @overload def simple_tag( + self, func: None = None, takes_context: bool | None = None, name: str | None = None + ) -> Callable[[_C], _C]: ... + @overload + def simple_block_tag( self, - func: None = ..., - takes_context: bool | None = ..., - name: str | None = ..., - ) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]: ... - # func is callable + func: _C, + takes_context: bool | None = None, + name: str | None = None, + end_name: str | None = None, + ) -> _C: ... @overload - def simple_tag( + def simple_block_tag( + self, + func: None = None, + takes_context: bool | None = None, + name: str | None = None, + end_name: str | None = None, + ) -> Callable[[_C], _C]: ... + @overload + def inclusion_tag( self, - func: Callable[_P, _T], - takes_context: bool | None = ..., - name: str | None = ..., - ) -> Callable[_P, _T]: ... + filename: Template | str, + func: _C, + takes_context: bool | None = None, + name: str | None = None, + ) -> _C: ... + @overload def inclusion_tag( self, filename: Template | str, - func: None = ..., - takes_context: bool | None = ..., - name: str | None = ..., - ) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]: ... + func: None = None, + takes_context: bool | None = None, + name: str | None = None, + ) -> Callable[[_C], _C]: ... class TagHelperNode(Node): - func: Any = ... - takes_context: Any = ... - args: Any = ... - kwargs: Any = ... + func: Any + takes_context: Any + args: Any + kwargs: Any def __init__( self, func: Callable[..., Any], @@ -118,16 +105,14 @@ class TagHelperNode(Node): args: list[FilterExpression], kwargs: dict[str, FilterExpression], ) -> None: ... - def get_resolved_arguments(self, context: Context) -> tuple[list[int], dict[str, SafeText | int]]: ... + def get_resolved_arguments(self, context: Context) -> tuple[list[int], dict[str, SafeString | int]]: ... class SimpleNode(TagHelperNode): args: list[FilterExpression] func: Callable[..., Any] kwargs: dict[str, FilterExpression] - origin: Origin takes_context: bool | None - token: Token - target_var: str | None = ... + target_var: str | None def __init__( self, func: Callable[..., Any], @@ -137,14 +122,22 @@ class SimpleNode(TagHelperNode): target_var: str | None, ) -> None: ... +class SimpleBlockNode(SimpleNode): + nodelist: NodeList + + def __init__( + self, + nodelist: NodeList, + *args: Any, + **kwargs: Any, + ) -> None: ... + class InclusionNode(TagHelperNode): args: list[FilterExpression] func: Callable[..., Any] kwargs: dict[str, FilterExpression] - origin: Origin takes_context: bool | None - token: Token - filename: Template | str = ... + filename: Template | str def __init__( self, func: Callable[..., Any], @@ -156,13 +149,13 @@ class InclusionNode(TagHelperNode): def parse_bits( parser: Parser, - bits: list[str], - params: list[str], + bits: Iterable[str], + params: Sequence[str], varargs: str | None, varkw: str | None, - defaults: tuple[bool | str] | None, - kwonly: list[str], - kwonly_defaults: dict[str, int] | None, + defaults: Sized | None, + kwonly: Collection[str], + kwonly_defaults: Mapping[str, int] | None, takes_context: bool | None, name: str, ) -> tuple[list[FilterExpression], dict[str, FilterExpression]]: ... diff --git a/django-stubs/template/loader.pyi b/django-stubs/template/loader.pyi index 2806c1e9f..284f0a4d1 100644 --- a/django-stubs/template/loader.pyi +++ b/django-stubs/template/loader.pyi @@ -1,17 +1,17 @@ +from collections.abc import Mapping, Sequence from typing import Any from django.http.request import HttpRequest -from django.template.backends.base import _BaseTemplate from django.template.exceptions import TemplateDoesNotExist as TemplateDoesNotExist -from django.utils.safestring import SafeText +from django.utils.safestring import SafeString -from . import engines as engines +from .backends.base import _EngineTemplate -def get_template(template_name: str, using: str | None = ...) -> _BaseTemplate: ... -def select_template(template_name_list: list[str] | str, using: str | None = ...) -> _BaseTemplate: ... +def get_template(template_name: str, using: str | None = None) -> _EngineTemplate: ... +def select_template(template_name_list: Sequence[str] | str, using: str | None = None) -> Any: ... def render_to_string( - template_name: list[str] | str, - context: dict[str, Any] | None = ..., - request: HttpRequest | None = ..., - using: str | None = ..., -) -> SafeText: ... + template_name: Sequence[str] | str, + context: Mapping[str, Any] | None = None, + request: HttpRequest | None = None, + using: str | None = None, +) -> SafeString: ... diff --git a/django-stubs/template/response.pyi b/django-stubs/template/response.pyi index 3aa576a6f..a62a25c15 100644 --- a/django-stubs/template/response.pyi +++ b/django-stubs/template/response.pyi @@ -1,7 +1,7 @@ import functools -from collections.abc import Callable, Sequence +from collections.abc import Callable, Iterator, Sequence from http.cookies import SimpleCookie -from typing import Any +from typing import Any, TypeAlias from django.core.handlers.wsgi import WSGIRequest from django.http import HttpResponse @@ -9,36 +9,43 @@ from django.http.request import HttpRequest from django.template.base import Template from django.template.context import RequestContext from django.test.client import Client +from typing_extensions import override + +from .backends.base import _EngineTemplate + +_TemplateForResponseT: TypeAlias = Sequence[str] | _EngineTemplate | str class ContentNotRenderedError(Exception): ... class SimpleTemplateResponse(HttpResponse): - content: Any = ... + content: Any closed: bool cookies: SimpleCookie status_code: int - rendering_attrs: Any = ... - template_name: list[str] | Template | str = ... - context_data: dict[str, Any] | None = ... - using: str | None = ... + rendering_attrs: Any + template_name: _TemplateForResponseT + context_data: dict[str, Any] | None + using: str | None def __init__( self, - template: list[str] | Template | str, + template: _TemplateForResponseT, context: dict[str, Any] | None = ..., content_type: str | None = ..., status: int | None = ..., charset: str | None = ..., using: str | None = ..., + headers: dict[str, Any] | None = ..., ) -> None: ... - def resolve_template(self, template: Sequence[str] | Template | str) -> Template: ... + def resolve_template(self, template: _TemplateForResponseT) -> _EngineTemplate: ... def resolve_context(self, context: dict[str, Any] | None) -> dict[str, Any] | None: ... @property def rendered_content(self) -> str: ... - def add_post_render_callback(self, callback: Callable[..., Any]) -> None: ... + def add_post_render_callback(self, callback: Callable) -> None: ... def render(self) -> SimpleTemplateResponse: ... @property def is_rendered(self) -> bool: ... - def __iter__(self) -> Any: ... + @override + def __iter__(self) -> Iterator[Any]: ... class TemplateResponse(SimpleTemplateResponse): client: Client @@ -47,22 +54,22 @@ class TemplateResponse(SimpleTemplateResponse): context_data: dict[str, Any] | None cookies: SimpleCookie csrf_cookie_set: bool - json: functools.partial[Any] - redirect_chain: list[tuple[str, int]] - request: dict[str, int | str] + json: functools.partial + _request: HttpRequest status_code: int - template_name: list[str] | Template | str + template_name: _TemplateForResponseT templates: list[Template] using: str | None wsgi_request: WSGIRequest - rendering_attrs: Any = ... + rendering_attrs: Any def __init__( self, request: HttpRequest, - template: list[str] | Template | str, + template: _TemplateForResponseT, context: dict[str, Any] | None = ..., content_type: str | None = ..., status: int | None = ..., - charset: None = ..., + charset: str | None = ..., using: str | None = ..., + headers: dict[str, Any] | None = ..., ) -> None: ... diff --git a/django-stubs/test/utils.pyi b/django-stubs/test/utils.pyi index 647a7ffd2..0f98bf128 100644 --- a/django-stubs/test/utils.pyi +++ b/django-stubs/test/utils.pyi @@ -20,9 +20,9 @@ from typing_extensions import Self, TypeVar, override _TestClass: TypeAlias = type[SimpleTestCase] -_DecoratedTest: TypeAlias = Callable[..., Any] | _TestClass +_DecoratedTest: TypeAlias = Callable | _TestClass _DT = TypeVar("_DT", bound=_DecoratedTest) -_C = TypeVar("_C", bound=Callable[..., Any]) # Any callable +_C = TypeVar("_C", bound=Callable) # Any callable TZ_SUPPORT: bool @@ -81,13 +81,11 @@ class modify_settings(override_settings): class override_system_checks(TestContextDecorator): registry: CheckRegistry - new_checks: list[Callable[..., Any]] - deployment_checks: list[Callable[..., Any]] | None - def __init__( - self, new_checks: list[Callable[..., Any]], deployment_checks: list[Callable[..., Any]] | None = ... - ) -> None: ... - old_checks: set[Callable[..., Any]] - old_deployment_checks: set[Callable[..., Any]] + new_checks: list[Callable] + deployment_checks: list[Callable] | None + def __init__(self, new_checks: list[Callable], deployment_checks: list[Callable] | None = ...) -> None: ... + old_checks: set[Callable] + old_deployment_checks: set[Callable] class CaptureQueriesContext(_SupportsContains[dict[str, str]]): connection: BaseDatabaseWrapper @@ -110,13 +108,13 @@ class CaptureQueriesContext(_SupportsContains[dict[str, str]]): class ignore_warnings(TestContextDecorator): ignore_kwargs: dict[str, Any] - filter_func: Callable[..., Any] + filter_func: Callable def __init__(self, **kwargs: Any) -> None: ... - catch_warnings: AbstractContextManager[list[Any] | None] + catch_warnings: AbstractContextManager[list | None] requires_tz_support: Any -def isolate_lru_cache(lru_cache_object: Callable[..., Any]) -> AbstractContextManager[None]: ... +def isolate_lru_cache(lru_cache_object: Callable) -> AbstractContextManager[None]: ... class override_script_prefix(TestContextDecorator): prefix: str @@ -174,7 +172,7 @@ def teardown_databases( ) -> None: ... def require_jinja2(test_func: _C) -> _C: ... def register_lookup( - field: type[RegisterLookupMixin], *lookups: type[Lookup[Any] | Transform], lookup_name: str | None = ... + field: type[RegisterLookupMixin], *lookups: type[Lookup | Transform], lookup_name: str | None = ... ) -> AbstractContextManager[None]: ... def garbage_collect() -> None: ... diff --git a/django-stubs/utils/choices.pyi b/django-stubs/utils/choices.pyi new file mode 100644 index 000000000..6e88c3a73 --- /dev/null +++ b/django-stubs/utils/choices.pyi @@ -0,0 +1,42 @@ +from collections.abc import Callable, Iterable, Iterator, Mapping +from typing import Any, Protocol, TypeAlias, type_check_only + +from django.db.models import Choices +from typing_extensions import TypeVar + +_Choice: TypeAlias = tuple[Any, Any] +_ChoiceNamedGroup: TypeAlias = tuple[str, Iterable[_Choice]] +_Choices: TypeAlias = Iterable[_Choice | _ChoiceNamedGroup] +_ChoicesMapping: TypeAlias = Mapping[Any, Any] +_ChoicesInput: TypeAlias = _Choices | _ChoicesMapping | type[Choices] | Callable[[], _Choices | _ChoicesMapping] # noqa: PYI047 + +@type_check_only +class _ChoicesCallable(Protocol): + def __call__(self) -> _Choices: ... + +class BaseChoiceIterator: + def __getitem__(self, index: int) -> _Choice | _ChoiceNamedGroup: ... + def __iter__(self) -> Iterator[_Choice | _ChoiceNamedGroup]: ... + +class BlankChoiceIterator(BaseChoiceIterator): + choices: _Choices + blank_choice: _Choices + def __init__(self, choices: _Choices, blank_choice: _Choices) -> None: ... + +class CallableChoiceIterator(BaseChoiceIterator): + func: _ChoicesCallable + def __init__(self, func: _ChoicesCallable) -> None: ... + +_V = TypeVar("_V") +_L = TypeVar("_L") + +def flatten_choices(choices: Iterable[tuple[_V, _L | Iterable[tuple[_V, _L]]]]) -> Iterator[tuple[_V, _L]]: ... +def normalize_choices(value: Any, *, depth: int = 0) -> Any: ... + +__all__ = [ + "BaseChoiceIterator", + "BlankChoiceIterator", + "CallableChoiceIterator", + "flatten_choices", + "normalize_choices", +] diff --git a/django-stubs/utils/datastructures.pyi b/django-stubs/utils/datastructures.pyi index ee81ca309..bb36ca9d2 100644 --- a/django-stubs/utils/datastructures.pyi +++ b/django-stubs/utils/datastructures.pyi @@ -1,72 +1,153 @@ -from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSet -from typing import Any, TypeVar, overload +from collections.abc import Iterable, Iterator, Mapping, MutableMapping, MutableSet +from typing import Any, Generic, NoReturn, Protocol, TypeAlias, overload, type_check_only -from typing_extensions import Self +from _typeshed import Incomplete +from typing_extensions import Self, TypeVar, override _K = TypeVar("_K") _V = TypeVar("_V") +_Z = TypeVar("_Z") + +# Unfortunately, there's often check `if isinstance(var, (list, tuple))` in django +# codebase. So we need sometimes to declare exactly list or tuple. +_ListOrTuple: TypeAlias = list[_K] | tuple[_K, ...] | tuple[()] # noqa: PYI047 + +@type_check_only +class _PropertyDescriptor(Generic[_K, _V]): + """ + This helper property descriptor allows defining asynmetric getter/setters + which mypy currently doesn't support with either: + + class HttpResponse: + @property + def content(...): ... + @property.setter + def content(...): ... + + or: + + class HttpResponse: + def _get_content(...): ... + def _set_content(...): ... + content = property(_get_content, _set_content) + """ + + def __get__(self, instance: Any, owner: Any | None) -> _V: ... + def __set__(self, instance: Any, value: _K) -> None: ... class OrderedSet(MutableSet[_K]): - dict: dict[_K, None] = ... - @overload - def __init__(self, iterable: Iterable[_K] | None) -> None: ... - @overload - def __init__(self, iterable: Iterable[Any] = ...) -> None: ... - def __contains__(self, item: object) -> bool: ... + dict: dict[_K, None] + def __init__(self, iterable: Iterable[_K] | None = None) -> None: ... + @override + def add(self, item: _K) -> None: ... + @override + def remove(self, item: _K) -> None: ... + @override + def discard(self, item: _K) -> None: ... + @override def __iter__(self) -> Iterator[_K]: ... + def __reversed__(self) -> Iterator[_K]: ... + @override + def __contains__(self, item: object) -> bool: ... + def __bool__(self) -> bool: ... + @override def __len__(self) -> int: ... - def add(self, x: _K) -> None: ... - def discard(self, item: _K) -> None: ... class MultiValueDictKeyError(KeyError): ... -class MultiValueDict(MutableMapping[_K, _V]): +class MultiValueDict(dict[_K, _V]): + @overload + def __init__(self, key_to_list_mapping: Mapping[_K, list[_V] | None]) -> None: ... @overload - def __init__( - self, - key_to_list_mapping: Mapping[_K, list[_V] | None], - ) -> None: ... + def __init__(self, key_to_list_mapping: Iterable[tuple[_K, list[_V]]] = ()) -> None: ... + @override + def __getitem__(self, key: _K) -> _V | list[object]: ... # type: ignore[override] + @override + def __setitem__(self, key: _K, value: _V) -> None: ... + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: MutableMapping[int, Incomplete]) -> Self: ... @overload - def __init__( - self, - key_to_list_mapping: Iterable[tuple[_K, list[_V]]], - ) -> None: ... + @override + def get(self, key: _K, default: None = None) -> _V | None: ... @overload - def __init__( - self, - key_to_list_mapping: Iterable[Any] = ..., - ) -> None: ... - def getlist(self, key: _K, default: list[_V] | None = ...) -> list[_V]: ... + def get(self, key: _K, default: _V) -> _V: ... + @overload + def get(self, key: _K, default: _Z) -> _V | _Z: ... + def getlist(self, key: _K, default: _Z | None = None) -> list[_V] | _Z: ... def setlist(self, key: _K, list_: list[_V]) -> None: ... - def setlistdefault(self, key: _K, default_list: list[_V] | None = ...) -> list[_V]: ... + @overload + @override + def setdefault(self: MultiValueDict[_K, _V | None], key: _K, default: None = None) -> _V | None: ... + @overload + def setdefault(self, key: _K, default: _V) -> _V: ... + def setlistdefault(self, key: _K, default_list: list[_V] | None = None) -> list[_V]: ... def appendlist(self, key: _K, value: _V) -> None: ... + @override + def items(self) -> Iterator[tuple[_K, _V | list[object]]]: ... # type: ignore[override] def lists(self) -> Iterable[tuple[_K, list[_V]]]: ... - def dict(self) -> dict[_K, _V | list[_V]]: ... # ty: ignore [invalid-type-form] + # Fake to make `values` work properly + @override + def values(self) -> Iterator[_V | list[object]]: ... # type: ignore[override] + @override def copy(self) -> Self: ... + def dict(self) -> dict[_K, _V | list[object]]: ... # ty: ignore[invalid-type-form] # These overrides are needed to convince mypy that this isn't an abstract class - def __delitem__(self, item: _K) -> None: ... - def __getitem__(self, key: _K) -> _V | list[object]: ... # type: ignore[override] - def __setitem__(self, k: _K, v: _V | list[_V]) -> None: ... + @override + def __delitem__(self, item: _K, /) -> None: ... + @override def __len__(self) -> int: ... + @override def __iter__(self) -> Iterator[_K]: ... class ImmutableList(tuple[_V, ...]): - warning: str = ... - def __init__(self, *args: Any, warning: str = ..., **kwargs: Any) -> None: ... - def __new__(cls, *args: Any, warning: str = ..., **kwargs: Any) -> Self: ... - def complain(self, *wargs: Any, **kwargs: Any) -> None: ... + warning: str + def __new__(cls, *args: object, warning: str = "ImmutableList object is immutable.", **kwargs: object) -> Self: ... + def complain(self, *args: object, **kwargs: object) -> NoReturn: ... + def __delitem__(self, *args: object, **kwargs: object) -> NoReturn: ... + def __delslice__(self, *args: object, **kwargs: object) -> NoReturn: ... + def __iadd__(self, *args: object, **kwargs: object) -> NoReturn: ... # type: ignore[misc] + def __imul__(self, *args: object, **kwargs: object) -> NoReturn: ... + def __setitem__(self, *args: object, **kwargs: object) -> NoReturn: ... + def __setslice__(self, *args: object, **kwargs: object) -> NoReturn: ... + def append(self, *args: object, **kwargs: object) -> NoReturn: ... + def extend(self, *args: object, **kwargs: object) -> NoReturn: ... + def insert(self, *args: object, **kwargs: object) -> NoReturn: ... + def pop(self, *args: object, **kwargs: object) -> NoReturn: ... + def remove(self, *args: object, **kwargs: object) -> NoReturn: ... + def sort(self, *args: object, **kwargs: object) -> NoReturn: ... + def reverse(self, *args: object, **kwargs: object) -> NoReturn: ... + +@type_check_only +class _ItemCallable(Protocol[_V]): + """Don't mess with arguments when assigning in class body in stub""" + + def __call__(self, value: _V, /) -> _V: ... class DictWrapper(dict[str, _V]): - func: Callable[[_V], _V] = ... - prefix: str = ... + func: _ItemCallable[_V] + prefix: str @overload - def __init__(self, data: Mapping[str, _V], func: Callable[[_V], _V], prefix: str) -> None: ... + def __init__(self, data: Mapping[str, _V], func: _ItemCallable[_V], prefix: str) -> None: ... @overload - def __init__(self, data: Iterable[tuple[str, _V]], func: Callable[[_V], _V], prefix: str) -> None: ... + def __init__(self, data: Iterable[tuple[str, _V]], func: _ItemCallable[_V], prefix: str) -> None: ... + @override + def __getitem__(self, key: str) -> _V: ... class CaseInsensitiveMapping(Mapping[str, _V]): - def __init__(self, data: Any) -> None: ... - def __getitem__(self, key: str) -> Any: ... + _store: dict[str, tuple[str, _V]] + def __init__(self, data: Mapping[str, _V] | Iterable[tuple[str, _V]]) -> None: ... + @override + def __getitem__(self, key: str) -> _V: ... + @override def __len__(self) -> int: ... + @override + def __eq__(self, other: object) -> bool: ... + @override def __iter__(self) -> Iterator[str]: ... def copy(self) -> Self: ... + +class DeferredSubDict(Generic[_K, _V]): + parent_dict: dict[Any, dict[_K, _V]] + deferred_key: Any + def __init__(self, parent_dict: dict[Any, dict[_K, _V]], deferred_key: Any) -> None: ... + def __getitem__(self, key: _K) -> _V: ... diff --git a/django-stubs/utils/decorators.pyi b/django-stubs/utils/decorators.pyi index d9d7b144c..57529eb68 100644 --- a/django-stubs/utils/decorators.pyi +++ b/django-stubs/utils/decorators.pyi @@ -1,9 +1,10 @@ from collections.abc import Callable, Iterable -from typing import Any, Protocol, TypeAlias, TypeVar, type_check_only +from typing import Any, Protocol, TypeAlias, type_check_only from django.http.response import HttpResponseBase from django.utils.deprecation import MiddlewareMixin from django.views.generic.base import View +from typing_extensions import TypeVar @type_check_only class _MiddlewareClass(Protocol): diff --git a/django-stubs/utils/html.pyi b/django-stubs/utils/html.pyi index 4f2c3c0a6..81b9b7eb5 100644 --- a/django-stubs/utils/html.pyi +++ b/django-stubs/utils/html.pyi @@ -1,46 +1,87 @@ from collections.abc import Iterable from html.parser import HTMLParser +from json import JSONEncoder +from re import Pattern from typing import Any -from django.utils.functional import _StrOrPromise -from django.utils.safestring import SafeData, SafeText - -TRAILING_PUNCTUATION_CHARS: str -WRAPPING_PUNCTUATION: Any -DOTS: Any -unencoded_ampersands_re: Any -word_split_re: Any -simple_url_re: Any -simple_url_2_re: Any - -def escape(text: Any) -> SafeText: ... -def escapejs(value: Any) -> SafeText: ... -def json_script(value: Any, element_id: str) -> SafeText: ... -def conditional_escape(text: _StrOrPromise | SafeData) -> str: ... -def format_html(format_string: str, *args: Any, **kwargs: Any) -> SafeText: ... -def format_html_join( - sep: str, - format_string: str, - args_generator: Iterable[Any] | Iterable[tuple[str]], -) -> SafeText: ... -def linebreaks(value: Any, autoescape: bool = ...) -> str: ... +from django.utils.functional import SimpleLazyObject, _StrOrPromise, cached_property +from django.utils.safestring import SafeData, SafeString +from typing_extensions import override + +VOID_ELEMENTS: frozenset[str] +MAX_URL_LENGTH: int +MAX_STRIP_TAGS_DEPTH: int +long_open_tag_without_closing_re: SimpleLazyObject + +def escape(text: Any) -> SafeString: ... +def escapejs(value: Any) -> SafeString: ... +def json_script(value: Any, element_id: str | None = None, encoder: type[JSONEncoder] | None = None) -> SafeString: ... + +# conditional_escape could use a protocol to be more precise, see https://github.com/typeddjango/django-stubs/issues/1474 +def conditional_escape(text: _StrOrPromise | SafeData) -> SafeString: ... +def format_html(format_string: str, *args: Any, **kwargs: Any) -> SafeString: ... +def format_html_join(sep: str, format_string: str, args_generator: Iterable[Iterable[Any]]) -> SafeString: ... +def linebreaks(value: Any, autoescape: bool = False) -> str: ... class MLStripper(HTMLParser): - fed: Any = ... + fed: list[str] def __init__(self) -> None: ... + @override def handle_data(self, d: str) -> None: ... + @override def handle_entityref(self, name: str) -> None: ... + @override def handle_charref(self, name: str) -> None: ... def get_data(self) -> str: ... def strip_tags(value: str) -> str: ... def strip_spaces_between_tags(value: str) -> str: ... def smart_urlquote(url: str) -> str: ... + +class CountsDict(dict[str, Any]): + word: str + def __init__(self, *args: Any, word: str, **kwargs: Any) -> None: ... + def __missing__(self, key: str) -> Any: ... + +class Urlizer: + trailing_punctuation_chars: str + wrapping_punctuation: list[tuple[str, str]] + word_split_re: Pattern[str] | SimpleLazyObject + simple_url_re: Pattern[str] | SimpleLazyObject + simple_url_2_re: Pattern[str] | SimpleLazyObject + mailto_template: str + url_template: str + def __call__( + self, + text: _StrOrPromise | SafeData, + trim_url_limit: int | None = None, + nofollow: bool = False, + autoescape: bool = False, + ) -> str: ... + def handle_word( + self, + word: str, + *, + safe_input: bool, + trim_url_limit: int | None = None, + nofollow: bool = False, + autoescape: bool = False, + ) -> str: ... + def trim_url(self, x: str, *, limit: int | None) -> str: ... + def trim_punctuation(self, word: str) -> tuple[str, str, str]: ... + @staticmethod + def is_email_simple(value: str) -> bool: ... + @cached_property + def wrapping_punctuation_openings(self) -> str: ... + @cached_property + def trailing_punctuation_chars_no_semicolon(self) -> str: ... + @cached_property + def trailing_punctuation_chars_has_semicolon(self) -> bool: ... + +urlizer: Urlizer + def urlize( - text: _StrOrPromise | SafeData, - trim_url_limit: int | None = ..., - nofollow: bool = ..., - autoescape: bool = ..., + text: _StrOrPromise | SafeData, trim_url_limit: int | None = None, nofollow: bool = False, autoescape: bool = False ) -> str: ... def avoid_wrapping(value: str) -> str: ... -def html_safe(klass: Any) -> Any: ... +def html_safe(klass: type) -> type: ... diff --git a/django-stubs/utils/safestring.pyi b/django-stubs/utils/safestring.pyi index 0e8e485c4..a1c279f2d 100644 --- a/django-stubs/utils/safestring.pyi +++ b/django-stubs/utils/safestring.pyi @@ -1,20 +1,28 @@ -from typing import Any, overload +from collections.abc import Callable +from typing import Any, TypeAlias, overload -from typing_extensions import Self +from django.utils.functional import _StrOrPromise +from typing_extensions import Self, TypeVar, override + +_SD = TypeVar("_SD", bound=SafeData) class SafeData: def __html__(self) -> Self: ... -class SafeText(str, SafeData): +class SafeString(str, SafeData): @overload - def __add__(self, rhs: SafeText) -> SafeText: ... + @override + def __add__(self, rhs: SafeString) -> SafeString: ... @overload def __add__(self, rhs: str) -> str: ... - @overload - def __iadd__(self, rhs: SafeText) -> SafeText: ... - @overload - def __iadd__(self, rhs: str) -> str: ... -SafeString = SafeText +SafeText: TypeAlias = SafeString + +_C = TypeVar("_C", bound=Callable[..., Any]) -def mark_safe(s: Any) -> SafeText: ... +@overload +def mark_safe(s: _SD) -> _SD: ... +@overload +def mark_safe(s: _C) -> _C: ... +@overload +def mark_safe(s: _StrOrPromise) -> SafeString: ... diff --git a/django-stubs/views/debug.pyi b/django-stubs/views/debug.pyi index 712c671bb..19a149bf8 100644 --- a/django-stubs/views/debug.pyi +++ b/django-stubs/views/debug.pyi @@ -12,7 +12,7 @@ from django.utils.safestring import SafeString DEBUG_ENGINE: Engine class CallableSettingWrapper: - def __init__(self, callable_setting: Callable[..., Any] | type[Any]) -> None: ... + def __init__(self, callable_setting: Callable | type[Any]) -> None: ... class ExceptionCycleWarning(UserWarning): ... diff --git a/django-stubs/views/decorators/cache.pyi b/django-stubs/views/decorators/cache.pyi index cb6e5a087..c8e2e91db 100644 --- a/django-stubs/views/decorators/cache.pyi +++ b/django-stubs/views/decorators/cache.pyi @@ -1,5 +1,7 @@ from collections.abc import Callable -from typing import Any, TypeVar +from typing import Any + +from typing_extensions import TypeVar _F = TypeVar("_F", bound=Callable[..., Any]) diff --git a/django-stubs/views/decorators/clickjacking.pyi b/django-stubs/views/decorators/clickjacking.pyi index f0ba61c7e..6a6bdcf8d 100644 --- a/django-stubs/views/decorators/clickjacking.pyi +++ b/django-stubs/views/decorators/clickjacking.pyi @@ -1,5 +1,7 @@ from collections.abc import Callable -from typing import Any, TypeVar +from typing import Any + +from typing_extensions import TypeVar _F = TypeVar("_F", bound=Callable[..., Any]) diff --git a/django-stubs/views/decorators/debug.pyi b/django-stubs/views/decorators/debug.pyi index 240bd0957..8af380374 100644 --- a/django-stubs/views/decorators/debug.pyi +++ b/django-stubs/views/decorators/debug.pyi @@ -1,5 +1,7 @@ from collections.abc import Callable -from typing import Any, Literal, TypeVar +from typing import Any, Literal + +from typing_extensions import TypeVar _F = TypeVar("_F", bound=Callable[..., Any]) diff --git a/django-stubs/views/decorators/vary.pyi b/django-stubs/views/decorators/vary.pyi index 7c8b1d159..ffa80eb92 100644 --- a/django-stubs/views/decorators/vary.pyi +++ b/django-stubs/views/decorators/vary.pyi @@ -1,5 +1,7 @@ from collections.abc import Callable -from typing import Any, TypeVar +from typing import Any + +from typing_extensions import TypeVar _F = TypeVar("_F", bound=Callable[..., Any]) diff --git a/django-stubs/views/i18n.pyi b/django-stubs/views/i18n.pyi index fe00560ad..48031f8cb 100644 --- a/django-stubs/views/i18n.pyi +++ b/django-stubs/views/i18n.pyi @@ -14,7 +14,7 @@ def set_language(request: HttpRequest) -> HttpResponse: ... def get_formats() -> dict[str, list[str] | int | str]: ... class JavaScriptCatalog(View): - head: Callable[..., Any] + head: Callable domain: str packages: list[str] | None translation: DjangoTranslation diff --git a/mypy.ini b/mypy.ini index 0749c850f..3781d7952 100644 --- a/mypy.ini +++ b/mypy.ini @@ -7,7 +7,7 @@ disallow_any_unimported=True disallow_any_expr=False disallow_any_decorated=True disallow_any_explicit=False -disallow_any_generics=True +disallow_any_generics=False disallow_subclassing_any=True disallow_untyped_calls=True diff --git a/pyproject.toml b/pyproject.toml index 7cc22b967..af39bf8d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,6 @@ dev = [ "ruff>=0.1.0", "tomlkit>=0.12.1", "pre-commit>=4.3.0", - "ast-grep-cli>=0.40.5", ] diff --git a/pyrightconfig.json b/pyrightconfig.json index af8a657f9..a59932e2a 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -5,6 +5,7 @@ "reportUnnecessaryIsInstance": false, "reportUnnecessaryComparison": false, "reportIncompatibleMethodOverride": false, + "reportMissingTypeArgument": false, "reportUnknownArgumentType": "none", "reportUnknownLambdaType": "none", "reportUnknownMemberType": "none", diff --git a/s/fix-sync.yml b/s/fix-sync.yml deleted file mode 100644 index 920ba0f10..000000000 --- a/s/fix-sync.yml +++ /dev/null @@ -1,111 +0,0 @@ -# ast-grep rules to fix common bare generics in synced stubs. -id: fix-callable-type -language: python -rule: - pattern: Callable - inside: - kind: type -fix: Callable[..., Any] ---- -id: fix-callable-typevar-bound -language: python -rule: - all: - - pattern: Callable - - inside: - stopBy: end - pattern: TypeVar($NAME, bound=$$$) - - not: - inside: - kind: generic_type - - not: - inside: - stopBy: end - pattern: Callable[$$$] -fix: Callable[..., Any] ---- -id: fix-callable-union-before -language: python -rule: - pattern: Callable - precedes: - pattern: "|" -fix: Callable[..., Any] ---- -id: fix-callable-union-after -language: python -rule: - pattern: Callable - follows: - pattern: "|" -fix: Callable[..., Any] ---- -id: fix-list-union-before -language: python -rule: - pattern: list - precedes: - pattern: "|" -fix: list[Any] ---- -id: fix-list-union-after -language: python -rule: - pattern: list - follows: - pattern: "|" -fix: list[Any] ---- -id: fix-lookup-type -language: python -rule: - pattern: Lookup - inside: - kind: type -fix: Lookup[Any] ---- -id: fix-lookup-union-before -language: python -rule: - pattern: Lookup - precedes: - pattern: "|" -fix: Lookup[Any] ---- -id: fix-lookup-union-after -language: python -rule: - pattern: Lookup - follows: - pattern: "|" -fix: Lookup[Any] ---- -id: fix-tuple-type -language: python -rule: - pattern: tuple - inside: - kind: type -fix: tuple[Any, ...] ---- -id: fix-tuple-union-before -language: python -rule: - pattern: tuple - precedes: - pattern: "|" -fix: tuple[Any, ...] ---- -id: fix-tuple-union-after -language: python -rule: - pattern: tuple - follows: - pattern: "|" -fix: tuple[Any, ...] ---- -id: fix-typevar-import -language: python -rule: - pattern: from typing_extensions import TypeVar -fix: from typing import TypeVar diff --git a/s/sync b/s/sync index d8d0abe4c..3edce2850 100755 --- a/s/sync +++ b/s/sync @@ -41,9 +41,6 @@ while IFS= read -r file || [ -n "$file" ]; do echo "Error: Failed to fetch $file from upstream" exit 1 fi - - # Fix bare Callable -> Callable[..., Any] using ast-grep - ast-grep scan --rule "$SCRIPT_DIR/fix-sync.yml" --update-all "$file" done < "$SYNC_FILES" uv run ruff format tests django-stubs diff --git a/s/sync-files.txt b/s/sync-files.txt index 5c636d07a..d8d1389a8 100644 --- a/s/sync-files.txt +++ b/s/sync-files.txt @@ -15,10 +15,10 @@ django-stubs/views/decorators/debug.pyi django-stubs/views/decorators/vary.pyi #django-stubs/http/request.pyi #django-stubs/contrib/sitemaps/views.pyi -#django-stubs/contrib/sitemaps/__init__.pyi +django-stubs/contrib/sitemaps/__init__.pyi django-stubs/contrib/sitemaps/apps.pyi -#django-stubs/contrib/gis/sitemaps/kml.pyi -#django-stubs/contrib/flatpages/sitemaps.pyi +django-stubs/contrib/gis/sitemaps/kml.pyi +django-stubs/contrib/flatpages/sitemaps.pyi #django-stubs/contrib/auth/forms.pyi django-stubs/core/validators.pyi #django-stubs/utils/deconstruct.pyi @@ -29,6 +29,7 @@ django-stubs/contrib/admin/decorators.pyi #django-stubs/db/models/aggregates.pyi #django-stubs/db/models/sql/compiler.pyi #django-stubs/db/models/sql/query.pyi +#django-stubs/contrib/admin/options.pyi #django-stubs/db/models/sql/subqueries.pyi django-stubs/db/models/sql/__init__.pyi #django-stubs/db/models/sql/where.pyi @@ -45,3 +46,27 @@ django-stubs/db/migrations/executor.pyi django-stubs/core/exceptions.pyi django-stubs/test/runner.pyi django-stubs/test/utils.pyi +django-stubs/utils/choices.pyi +#django-stubs/forms/models.pyi +django-stubs/forms/utils.pyi +django-stubs/forms/fields.pyi +django-stubs/forms/__init__.pyi +django-stubs/forms/boundfield.pyi +#django-stubs/forms/forms.pyi +django-stubs/forms/formsets.pyi +django-stubs/forms/renderers.pyi +#django-stubs/forms/widgets.pyi +#django-stubs/utils/datastructures.pyi +django-stubs/contrib/contenttypes/admin.pyi +django-stubs/contrib/sites/admin.pyi +django-stubs/contrib/redirects/admin.pyi +django-stubs/contrib/flatpages/admin.pyi +#django-stubs/template/response.pyi +django-stubs/template/backends/base.pyi +django-stubs/template/context.pyi +django-stubs/template/exceptions.pyi +django-stubs/template/library.pyi +django-stubs/template/loader.pyi +django-stubs/utils/safestring.pyi +#django-stubs/utils/html.pyi +django-stubs/core/signing.pyi diff --git a/uv.lock b/uv.lock index ba839da82..06a7b566a 100644 --- a/uv.lock +++ b/uv.lock @@ -22,21 +22,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" }, ] -[[package]] -name = "ast-grep-cli" -version = "0.42.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/c9/7787f03166a7b929f80cab49e3c1bb2a8937a757a019fb5331a8dee038d2/ast_grep_cli-0.42.2.tar.gz", hash = "sha256:ce778a10286c23b403b0611b63d97e03f9f98d238a56198e821711229226823a", size = 232340, upload-time = "2026-05-10T22:09:11.907Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/df/e82bb88929c449c7b94d6d7012c8cff782d7649ad2aa624a9cc8363a0aab/ast_grep_cli-0.42.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f158078525a770c5ec8298c3f0405bddd6d8a47ff0e22e893648a3422630595b", size = 15226223, upload-time = "2026-05-10T22:09:20.458Z" }, - { url = "https://files.pythonhosted.org/packages/2a/90/cd7e7a2bf2661b78f10fccf23efd6181d859b2a7708936a0b0e7af4c5a9a/ast_grep_cli-0.42.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:683dc491a072164a031b8e0800561229eef8ae0c7c3e90cb5bc9f4c34ffbd494", size = 7614498, upload-time = "2026-05-10T22:09:17.934Z" }, - { url = "https://files.pythonhosted.org/packages/42/db/04d26b1d89cc1a59cded67a564a1f6afa9de56fa3528ced1ca0cb5e359df/ast_grep_cli-0.42.2-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:26adbb32aed0093a37128cc7fa0230b13dc5f1b806e3b526479f55bef65946d2", size = 7461039, upload-time = "2026-05-10T22:09:04.743Z" }, - { url = "https://files.pythonhosted.org/packages/45/7b/73a0b1537f90cd56a3dc563dcf6c8857d8e37e472b27c3d1dfbc57545648/ast_grep_cli-0.42.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a2f4728d6cc75a67265afa0801dade15649eb9a5188333d1b0c51b4833f975d3", size = 7758664, upload-time = "2026-05-10T22:09:13.55Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f1/82b5568035bba69b9244e4f37732ca46248f7be43812b939b1cb9c7f6545/ast_grep_cli-0.42.2-py3-none-win32.whl", hash = "sha256:c31ff738565039bd84b498e4cc1a4f887bc34e214e3459e32b920e9251e9587a", size = 7340932, upload-time = "2026-05-10T22:09:06.987Z" }, - { url = "https://files.pythonhosted.org/packages/d0/b2/4c83ca5eb017134ac7958d0c55bd85cfd16b9b4b5f5c16e91327d644a710/ast_grep_cli-0.42.2-py3-none-win_amd64.whl", hash = "sha256:1192fb01359f5685310cce5adff62f31cee9e98315c0577821c9b49ebb830c9e", size = 7800725, upload-time = "2026-05-10T22:09:10.064Z" }, - { url = "https://files.pythonhosted.org/packages/af/77/7f5ab54535b7ca4f92a20b7f6628d25d9248dce28fd78040935504bb77bd/ast_grep_cli-0.42.2-py3-none-win_arm64.whl", hash = "sha256:04af685e244a974654a1e6d1c73a65dd624eb46495351e4eeb06f06bb099ac9a", size = 7504352, upload-time = "2026-05-10T22:09:15.868Z" }, -] - [[package]] name = "ast-serialize" version = "0.3.0" @@ -123,7 +108,6 @@ source = { editable = "." } [package.dev-dependencies] dev = [ - { name = "ast-grep-cli" }, { name = "django" }, { name = "mypy" }, { name = "pre-commit" }, @@ -141,7 +125,6 @@ dev = [ [package.metadata.requires-dev] dev = [ - { name = "ast-grep-cli", specifier = ">=0.40.5" }, { name = "django", specifier = ">=5.2,<6.0" }, { name = "mypy", specifier = ">=2.1.0" }, { name = "pre-commit", specifier = ">=4.3.0" }, @@ -527,27 +510,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" }, - { url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" }, - { url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" }, - { url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" }, - { url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" }, - { url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" }, - { url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" }, - { url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" }, - { url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" }, - { url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" }, - { url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" }, - { url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" }, - { url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" }, +version = "0.15.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/21/a7d5c126d5b557715ef81098f3db2fe20f622a039ff2e626af28d674ab80/ruff-0.15.13.tar.gz", hash = "sha256:f9d89f17f7ba7fb2ed42921f0df75da797a9a5d71bc39049e2c687cf2baf44b7", size = 4678180, upload-time = "2026-05-14T13:44:37.869Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/61/11d458dc6ac22504fd8e237b29dfd40504c7fbbcc8930402cfe51a8e63ed/ruff-0.15.13-py3-none-linux_armv6l.whl", hash = "sha256:444b580fc72fd6887e650acd3e575e18cdc79dbcf42fb4030b491057921f61f8", size = 10738279, upload-time = "2026-05-14T13:44:18.7Z" }, + { url = "https://files.pythonhosted.org/packages/86/ca/caa871ee7be718c45256fada4e16a218ee3e33f0c4a46b729a60a24912e6/ruff-0.15.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6590d009e7cb7ebf36f83dbdd44a3fa48a0994ff6f1cdc1b08006abe58f98dc7", size = 11124798, upload-time = "2026-05-14T13:44:06.427Z" }, + { url = "https://files.pythonhosted.org/packages/d3/19/43f5f2e568dddde567fc41f8471f9432c09563e19d3e617a48cfa52f8f0a/ruff-0.15.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1c26d2f66163deeb6e08d8b39fbbe983ce3c71cea06a6d7591cfd1421793c629", size = 10460761, upload-time = "2026-05-14T13:44:04.375Z" }, + { url = "https://files.pythonhosted.org/packages/99/df/cf938cd6de3003178f03ad7c1ea2a6c099468c03a35037985070b37e76be/ruff-0.15.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbd6f94b434f896308e4d57fb7bfde0d02b99f7a64b3bdab0fdfa6a864203a5", size = 10804451, upload-time = "2026-05-14T13:44:25.221Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7d/5d0973129b154ded2225729169d7068f26b467760b146493fde138415f23/ruff-0.15.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3259f3be4d181bda591da5db2571aed6853c6a048157756448020bc6c5cd22", size = 10534285, upload-time = "2026-05-14T13:44:08.888Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e3/6b999bbc66cd51e5f073842bc2a3995e99c5e0e72e16b15e7261f7abf57a/ruff-0.15.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae9c17e5eb4430c154e76abc25d79a318190f5a997f38fb6b114416c5319ffc9", size = 11312063, upload-time = "2026-05-14T13:44:11.274Z" }, + { url = "https://files.pythonhosted.org/packages/af/5a/642639e9f5db04f1e97fbd6e091c6fd20725bdf072fb114d00eefb9e6eb8/ruff-0.15.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e2e39bff6c341f4b577a21b801326fab0b11847f48fcaa83f00a113c9b3cb55", size = 12183079, upload-time = "2026-05-14T13:44:01.634Z" }, + { url = "https://files.pythonhosted.org/packages/19/4c/7585735f6b53b0f12de13618b2f7d250a844f018822efc899df2e7b8295f/ruff-0.15.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8d9a8e08013542e94d3220bc5b62cc3e5ef87c5f74bff367d3fac14fab013e6", size = 11440833, upload-time = "2026-05-14T13:43:59.043Z" }, + { url = "https://files.pythonhosted.org/packages/e8/31/bf1a0803d077e679cfeee5f2f67290a0fa79c7385b5d9a8c17b9db2c48f0/ruff-0.15.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc411dfebe5eebe55ce041c6ae080eb7668955e866daa2fbb16692a784f1c4ca", size = 11434486, upload-time = "2026-05-14T13:44:27.761Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4e/62c9b999875d4f14db80f277c030578f5e249c9852d65b7ac7ad0b43c041/ruff-0.15.13-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:768494eb08b9cee54e2fd27969966f74db5a57f6eaa7a90fcb3306af34dfc4bd", size = 11385189, upload-time = "2026-05-14T13:44:13.704Z" }, + { url = "https://files.pythonhosted.org/packages/fc/89/7e959047a104df3eb12863447c110140191fc5b6c4f379ea2e803fcdb0e4/ruff-0.15.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fb75f9a3a7e42ffe117d734494e6c5e5cb3565d66e12612cb63d0e572a41a5b6", size = 10781380, upload-time = "2026-05-14T13:43:56.734Z" }, + { url = "https://files.pythonhosted.org/packages/ff/52/5fd18f3b88cab63e88aa11516b3b4e1e5f720e5c330f8dbe5c26210f41f8/ruff-0.15.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8cb74dd33bb2f6613faf7fc03b660053b5ac4f80e706d5788c6335e2a8048d51", size = 10540605, upload-time = "2026-05-14T13:44:20.748Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e0/9e35f338990d3e41a82875ff7053ffe97541dae81c9d02143177f381d572/ruff-0.15.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7ef823f817fcd191dc934e984be9cf4094f808effa16f2542ad8e821ba02bbf2", size = 11036554, upload-time = "2026-05-14T13:44:16.256Z" }, + { url = "https://files.pythonhosted.org/packages/c2/13/070fb048c24080fba188f66371e2a92785be257ad02242066dc7255ac6e9/ruff-0.15.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f345a13937bd7f09f6f5d19fa0721b0c103e00e7f62bc67089a8e5e037719e0b", size = 11528133, upload-time = "2026-05-14T13:44:22.808Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8c/b1e1666aef7fc6555094d73ae6cd981701781ae85b97ceefc0eebd0b4668/ruff-0.15.13-py3-none-win32.whl", hash = "sha256:4044f94208b3b05ba0fc4a4abd0558cf4d6459bd18325eead7fd8cc66f909b41", size = 10721455, upload-time = "2026-05-14T13:44:35.697Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a6/870a3e8a50590bb92be184ad928c2922f088b00d9dc5c5ec7b924ee08c22/ruff-0.15.13-py3-none-win_amd64.whl", hash = "sha256:7064884d442b7d477b4e7473d12da7f08851d2b1982763c5d3f388a19468a1a4", size = 11900409, upload-time = "2026-05-14T13:44:30.389Z" }, + { url = "https://files.pythonhosted.org/packages/9b/36/9c015cd052fca743dae8cb2aeb16b551444787467db42ceab0fc968865af/ruff-0.15.13-py3-none-win_arm64.whl", hash = "sha256:2471da9bd1068c8c064b5fd9c0c4b6dddffd6369cb1cd68b29993b1709ff1b21", size = 11179336, upload-time = "2026-05-14T13:44:33.026Z" }, ] [[package]]