Skip to content

Commit fc18a3f

Browse files
committed
Merge remote-tracking branch 'upstream/master' into field-widget-rework
2 parents cdd1d73 + 676509a commit fc18a3f

File tree

169 files changed

+2157
-804
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

169 files changed

+2157
-804
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,14 @@ jobs:
7070
- name: Run pytest tests
7171
run: PYTHONPATH='.' pytest --num-shards=4 --shard-id=${{ matrix.shard }} -n auto tests
7272
- name: Run mypy on the test cases
73-
run: mypy --strict tests/assert_type
73+
run: mypy --strict tests
7474

7575
stubtest:
7676
timeout-minutes: 10
7777
runs-on: ubuntu-latest
7878
strategy:
7979
matrix:
80-
python-version: ['3.12']
80+
python-version: ['3.12', '3.13']
8181
fail-fast: false
8282
steps:
8383
- uses: actions/checkout@v4

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ repos:
1717
args: [--fix=lf]
1818
- id: check-case-conflict
1919
- repo: https://github.com/astral-sh/ruff-pre-commit
20-
rev: v0.11.5
20+
rev: v0.11.8
2121
hooks:
2222
- id: ruff
2323
args: ["--fix", "--exit-non-zero-on-fix"]

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ We rely on different `django` and `mypy` versions:
4949

5050
| django-stubs | Mypy version | Django version | Django partial support | Python version |
5151
|----------------|--------------|----------------|------------------------|----------------|
52+
| 5.2.0 | 1.13+ | 5.2 | 5.1 | 3.10 - 3.13 |
5253
| 5.1.3 | 1.13+ | 5.1 | 4.2 | 3.9 - 3.13 |
5354
| 5.1.2 | 1.13+ | 5.1 | 4.2 | 3.9 - 3.13 |
5455
| 5.1.1 | 1.13.x | 5.1 | 4.2 | 3.8 - 3.12 |
@@ -163,6 +164,11 @@ This happens because these Django classes do not support [`__class_getitem__`](h
163164

164165
You can add extra types to patch with `django_stubs_ext.monkeypatch(extra_classes=[YourDesiredType])`
165166

167+
**If you use generic symbols in `django.contrib.auth.forms`**, you will have to do the monkeypatching
168+
again in your first [`AppConfig.ready`](https://docs.djangoproject.com/en/5.2/ref/applications/#django.apps.AppConfig.ready).
169+
This is currently required because `django.contrib.auth.forms` cannot be imported until django is initialized.
170+
171+
166172
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.
167173

168174
### How can I create a HttpRequest that's guaranteed to have an authenticated user?

django-stubs/apps/__init__.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
from .config import AppConfig as AppConfig
22
from .registry import apps as apps
3+
4+
__all__ = ["AppConfig", "apps"]

django-stubs/conf/global_settings.pyi

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ from re import Pattern
33

44
# This is defined here as a do-nothing function because we can't import
55
# django.utils.translation -- that module depends on the settings.
6-
from typing import Any, Literal, Protocol, TypeAlias, type_check_only
6+
from typing import Any, Literal, Protocol, TypeAlias, TypedDict, type_check_only
7+
8+
from typing_extensions import NotRequired
79

810
_Admins: TypeAlias = list[tuple[str, str]]
911

@@ -401,7 +403,12 @@ PASSWORD_RESET_TIMEOUT: int
401403
# upon login
402404
PASSWORD_HASHERS: list[str]
403405

404-
AUTH_PASSWORD_VALIDATORS: list[dict[str, str]]
406+
@type_check_only
407+
class _AuthPasswordValidatorsDict(TypedDict):
408+
NAME: str
409+
OPTIONS: NotRequired[dict[str, Any]]
410+
411+
AUTH_PASSWORD_VALIDATORS: list[_AuthPasswordValidatorsDict]
405412

406413
###########
407414
# SIGNING #

django-stubs/contrib/admin/__init__.pyi

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,28 @@ from .sites import AdminSite as AdminSite
2222
from .sites import site as site
2323

2424
def autodiscover() -> None: ...
25+
26+
__all__ = [
27+
"action",
28+
"display",
29+
"register",
30+
"ModelAdmin",
31+
"HORIZONTAL",
32+
"VERTICAL",
33+
"StackedInline",
34+
"TabularInline",
35+
"AdminSite",
36+
"site",
37+
"ListFilter",
38+
"SimpleListFilter",
39+
"FieldListFilter",
40+
"BooleanFieldListFilter",
41+
"RelatedFieldListFilter",
42+
"ChoicesFieldListFilter",
43+
"DateFieldListFilter",
44+
"AllValuesFieldListFilter",
45+
"EmptyFieldListFilter",
46+
"RelatedOnlyFieldListFilter",
47+
"ShowFacets",
48+
"autodiscover",
49+
]

django-stubs/contrib/admin/options.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ from django.forms.models import (
2525
ModelForm,
2626
ModelMultipleChoiceField,
2727
)
28-
from django.forms.widgets import Media
28+
from django.forms.widgets import Media, MediaDefiningClass
2929
from django.http.request import HttpRequest
3030
from django.http.response import HttpResponse, HttpResponseBase, HttpResponseRedirect
3131
from django.template.response import _TemplateForResponseT
@@ -84,7 +84,7 @@ _ListDisplayT: TypeAlias = _ListOrTuple[_DisplayT[_ModelT]]
8484

8585
# Options `form`, `list_display`, `list_display_links` and `actions` are not marked as `ClassVar` due to the
8686
# limitations of the current type system: `ClassVar` cannot contain type variables.
87-
class BaseModelAdmin(Generic[_ModelT]):
87+
class BaseModelAdmin(Generic[_ModelT], metaclass=MediaDefiningClass):
8888
autocomplete_fields: ClassVar[_ListOrTuple[str]]
8989
raw_id_fields: ClassVar[_ListOrTuple[str]]
9090
fields: ClassVar[_FieldGroups | None]

django-stubs/contrib/admin/templatetags/admin_list.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ from django.utils.safestring import SafeString
1313
from .base import InclusionAdminNode
1414

1515
register: Any
16-
DOT: str
1716

1817
def paginator_number(cl: ChangeList, i: int) -> SafeString: ...
1918
def pagination(cl: ChangeList) -> dict[str, Any]: ...

django-stubs/contrib/auth/models.pyi

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ _AnyUser: TypeAlias = _User | AnonymousUser
2424
# These are only needed for generic classes in order to bind to a specific implementation
2525
_AnyUserType = TypeVar("_AnyUserType", bound=_AnyUser) # noqa: PYI018
2626

27-
# do not use the alias `_User` so the bound remains at `AbstractUser`
28-
_UserType = TypeVar("_UserType", bound=AbstractUser)
27+
# do not use the alias `_User` so the bound remains at `AbstractBaseUser`
28+
_UserType = TypeVar("_UserType", bound=AbstractBaseUser)
2929

3030
def update_last_login(sender: _UserModel, user: _User, **kwargs: Any) -> None: ...
3131

@@ -79,16 +79,16 @@ class PermissionsMixin(models.Model):
7979
groups = models.ManyToManyField(Group)
8080
user_permissions = models.ManyToManyField(Permission)
8181

82-
def get_user_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
83-
async def aget_user_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
84-
def get_group_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
85-
async def aget_group_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
86-
def get_all_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
87-
async def aget_all_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
88-
def has_perm(self, perm: str, obj: _AnyUser | None = ...) -> bool: ...
89-
async def ahas_perm(self, perm: str, obj: _AnyUser | None = ...) -> bool: ...
90-
def has_perms(self, perm_list: Iterable[str], obj: _AnyUser | None = ...) -> bool: ...
91-
async def ahas_perms(self, perm_list: Iterable[str], obj: _AnyUser | None = ...) -> bool: ...
82+
def get_user_permissions(self, obj: Model | None = ...) -> set[str]: ...
83+
async def aget_user_permissions(self, obj: Model | None = ...) -> set[str]: ...
84+
def get_group_permissions(self, obj: Model | None = ...) -> set[str]: ...
85+
async def aget_group_permissions(self, obj: Model | None = ...) -> set[str]: ...
86+
def get_all_permissions(self, obj: Model | None = ...) -> set[str]: ...
87+
async def aget_all_permissions(self, obj: Model | None = ...) -> set[str]: ...
88+
def has_perm(self, perm: str, obj: Model | None = ...) -> bool: ...
89+
async def ahas_perm(self, perm: str, obj: Model | None = ...) -> bool: ...
90+
def has_perms(self, perm_list: Iterable[str], obj: Model | None = ...) -> bool: ...
91+
async def ahas_perms(self, perm_list: Iterable[str], obj: Model | None = ...) -> bool: ...
9292
def has_module_perms(self, app_label: str) -> bool: ...
9393
async def ahas_module_perms(self, app_label: str) -> bool: ...
9494

@@ -131,16 +131,16 @@ class AnonymousUser:
131131
def groups(self) -> EmptyManager[Group]: ...
132132
@property
133133
def user_permissions(self) -> EmptyManager[Permission]: ...
134-
def get_user_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
135-
async def aget_user_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
136-
def get_group_permissions(self, obj: _AnyUser | None = ...) -> set[Any]: ...
137-
async def aget_group_permissions(self, obj: _AnyUser | None = ...) -> set[Any]: ...
138-
def get_all_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
139-
async def aget_all_permissions(self, obj: _AnyUser | None = ...) -> set[str]: ...
140-
def has_perm(self, perm: str, obj: _AnyUser | None = ...) -> bool: ...
141-
async def ahas_perm(self, perm: str, obj: _AnyUser | None = ...) -> bool: ...
142-
def has_perms(self, perm_list: Iterable[str], obj: _AnyUser | None = ...) -> bool: ...
143-
async def ahas_perms(self, perm_list: Iterable[str], obj: _AnyUser | None = ...) -> bool: ...
134+
def get_user_permissions(self, obj: Model | None = ...) -> set[str]: ...
135+
async def aget_user_permissions(self, obj: Model | None = ...) -> set[str]: ...
136+
def get_group_permissions(self, obj: Model | None = ...) -> set[Any]: ...
137+
async def aget_group_permissions(self, obj: Model | None = ...) -> set[Any]: ...
138+
def get_all_permissions(self, obj: Model | None = ...) -> set[str]: ...
139+
async def aget_all_permissions(self, obj: Model | None = ...) -> set[str]: ...
140+
def has_perm(self, perm: str, obj: Model | None = ...) -> bool: ...
141+
async def ahas_perm(self, perm: str, obj: Model | None = ...) -> bool: ...
142+
def has_perms(self, perm_list: Iterable[str], obj: Model | None = ...) -> bool: ...
143+
async def ahas_perms(self, perm_list: Iterable[str], obj: Model | None = ...) -> bool: ...
144144
def has_module_perms(self, module: str) -> bool: ...
145145
async def ahas_module_perms(self, module: str) -> bool: ...
146146
@property

django-stubs/contrib/auth/views.pyi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ class LogoutView(RedirectURLMixin, TemplateView):
3131
extra_context: Any
3232
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
3333
def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
34-
def get_next_page(self) -> str | None: ...
3534

3635
def logout_then_login(request: HttpRequest, login_url: str | None = ...) -> HttpResponseRedirect: ...
3736
def redirect_to_login(
@@ -51,7 +50,6 @@ class PasswordResetView(PasswordContextMixin, FormView):
5150
title: Any
5251
token_generator: Any
5352

54-
INTERNAL_RESET_URL_TOKEN: str
5553
INTERNAL_RESET_SESSION_TOKEN: str
5654

5755
class PasswordResetDoneView(PasswordContextMixin, TemplateView):

django-stubs/contrib/contenttypes/fields.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class GenericRelation(ForeignObject[Any, Any]):
9090
def resolve_related_fields(self) -> list[tuple[Field, Field]]: ...
9191
def get_path_info(self, filtered_relation: FilteredRelation | None = ...) -> list[PathInfo]: ...
9292
def get_reverse_path_info(self, filtered_relation: FilteredRelation | None = ...) -> list[PathInfo]: ...
93+
def contribute_to_class(self, cls: type[Model], name: str, **kwargs: Any) -> None: ... # type: ignore[override]
9394
def get_content_type(self) -> ContentType: ...
9495
def get_extra_restriction(
9596
self, where_class: type[WhereNode], alias: str | None, remote_alias: str

django-stubs/contrib/contenttypes/models.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ class ContentType(models.Model):
1919
@property
2020
def name(self) -> str: ...
2121
def model_class(self) -> type[Model] | None: ...
22-
def get_object_for_this_type(self, **kwargs: Any) -> Model: ...
22+
def get_object_for_this_type(self, using: str | None = None, **kwargs: Any) -> Model: ...
2323
def get_all_objects_for_this_type(self, **kwargs: Any) -> QuerySet: ...
2424
def natural_key(self) -> tuple[str, str]: ...

django-stubs/contrib/gis/admin/__init__.pyi

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,18 @@ from django.contrib.admin import display as display
1010
from django.contrib.admin import register as register
1111
from django.contrib.admin import site as site
1212
from django.contrib.gis.admin.options import GISModelAdmin as GISModelAdmin
13+
14+
__all__ = [
15+
"HORIZONTAL",
16+
"VERTICAL",
17+
"AdminSite",
18+
"ModelAdmin",
19+
"StackedInline",
20+
"TabularInline",
21+
"action",
22+
"autodiscover",
23+
"display",
24+
"register",
25+
"site",
26+
"GISModelAdmin",
27+
]

django-stubs/contrib/gis/db/backends/base/features.pyi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class BaseSpatialFeatures:
55
has_spatialrefsys_table: bool
66
supports_add_srs_entry: bool
77
supports_geometry_field_introspection: bool
8+
supports_geography: bool
89
supports_3d_storage: bool
910
supports_3d_functions: bool
1011
supports_transform: bool
@@ -19,6 +20,10 @@ class BaseSpatialFeatures:
1920
supports_dwithin_distance_expr: bool
2021
supports_raster: bool
2122
supports_geometry_field_unique_index: bool
23+
can_alter_geometry_field: bool
24+
supports_tolerance_parameter: bool
25+
unsupported_geojson_options: set[str]
26+
empty_intersection_returns_none: bool
2227
@property
2328
def supports_bbcontains_lookup(self) -> bool: ...
2429
@property

django-stubs/contrib/gis/db/backends/base/operations.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ from django.utils.functional import cached_property
55
class BaseSpatialOperations:
66
postgis: bool
77
spatialite: bool
8+
mariadb: bool
89
mysql: bool
910
oracle: bool
1011
spatial_version: Any
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from typing import Any
2-
31
from django.contrib.gis.db.backends.base.features import BaseSpatialFeatures
42
from django.db.backends.mysql.features import DatabaseFeatures as MySQLDatabaseFeatures
53
from django.utils.functional import cached_property
@@ -13,9 +11,5 @@ class DatabaseFeatures(BaseSpatialFeatures, MySQLDatabaseFeatures):
1311
supports_transform: bool
1412
supports_null_geometries: bool
1513
supports_num_points_poly: bool
16-
@property
17-
def empty_intersection_returns_none(self) -> bool: ...
1814
@cached_property
1915
def supports_geometry_field_unique_index(self) -> bool: ... # type: ignore[override]
20-
@cached_property
21-
def django_test_skips(self) -> dict[str, Any]: ... # type: ignore[override]

django-stubs/contrib/gis/db/backends/mysql/operations.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
1212
geom_func_prefix: str
1313
Adapter: Any
1414
@cached_property
15-
def mariadb(self) -> bool: ...
15+
def mariadb(self) -> bool: ... # type: ignore[override]
1616
@cached_property
1717
def mysql(self) -> bool: ... # type: ignore[override]
1818
@cached_property

django-stubs/contrib/gis/db/backends/mysql/schema.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,3 @@ class MySQLGISSchemaEditor(DatabaseSchemaEditor):
1919
def create_model(self, model: type[Model]) -> None: ...
2020
def add_field(self, model: type[Model], field: Field) -> None: ...
2121
def remove_field(self, model: type[Model], field: Field) -> None: ...
22-
def create_spatial_indexes(self) -> None: ...

django-stubs/contrib/gis/db/backends/postgis/adapter.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,4 @@ class PostGISAdapter:
99
def __conform__(self, proto: Any) -> Any: ...
1010
def __eq__(self, other: object) -> bool: ...
1111
def __hash__(self) -> int: ...
12-
def prepare(self, conn: Any) -> None: ...
1312
def getquoted(self) -> Any: ...

0 commit comments

Comments
 (0)