Skip to content

Commit 2602ed8

Browse files
authored
Update mypy configuration (#2799)
* Remove redundant mypy configuration options - Remove `ignore_missing_imports = false` as it's the default - `check_untyped_defs = true` is covered by `strict = true` - `strict_bytes = true` is covered by `strict = true` * Move third-party overrides into affected test Instead of setting this globally, move it into the affected test. * Enable `exhaustive-match` error code * Enable `ignore-without-code` error code * Enable `possibly-undefined` error code * Enable `redundant-expr` error code * Enable `redundant-self` error code * Enable `truthy-bool` error code * Enable `truthy-iterable` error code * Enable `unimported-reveal` error code * Enable `unused-awaitable` error code * Disable `allow-redefinition` option
1 parent 0ee42e7 commit 2602ed8

Some content is hidden

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

67 files changed

+524
-211
lines changed

mypy.ini

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,33 @@
55
incremental = true
66

77
# Strictness:
8-
allow_redefinition = true
9-
check_untyped_defs = true
108
# TODO: add type args to all generics
119
disallow_any_generics = false
1210
# TODO: fix `Any` subclassing in `typeshed/builtins.pyi`
1311
disallow_subclassing_any = false
14-
ignore_missing_imports = false
1512
strict = true
16-
strict_bytes = true
1713
local_partial_types = true
1814
warn_unreachable = true
1915

2016
disable_error_code = empty-body
21-
enable_error_code = deprecated
17+
enable_error_code =
18+
deprecated,
19+
exhaustive-match,
20+
ignore-without-code,
21+
possibly-undefined,
22+
redundant-expr,
23+
redundant-self,
24+
truthy-bool,
25+
truthy-iterable,
26+
unimported-reveal,
27+
unused-awaitable,
2228

2329
show_traceback = true
2430

2531
plugins =
2632
mypy_django_plugin.main,
2733
mypy.plugins.proper_plugin
2834

29-
# Ignore incomplete hints in 3rd party stubs:
30-
[mypy-yaml.*]
31-
disallow_untyped_defs = false
32-
disallow_incomplete_defs = false
33-
3435
# Our settings:
3536
[mypy.plugins.django-stubs]
3637
django_settings_module = scripts.django_tests_settings

mypy_django_plugin/django/context.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,6 @@ def get_primary_key_field(self, model_cls: type[Model]) -> "Field[Any, Any]":
200200

201201
def get_expected_types(self, api: TypeChecker, model_cls: type[Model], *, method: str) -> dict[str, MypyType]:
202202
contenttypes_in_apps = self.apps_registry.is_installed("django.contrib.contenttypes")
203-
if contenttypes_in_apps:
204-
from django.contrib.contenttypes.fields import GenericForeignKey
205203

206204
expected_types = {}
207205
# add pk if not abstract=True
@@ -256,17 +254,20 @@ def get_expected_types(self, api: TypeChecker, model_cls: type[Model], *, method
256254

257255
expected_types[field_name] = model_set_type
258256

259-
elif contenttypes_in_apps and isinstance(field, GenericForeignKey):
260-
# it's generic, so cannot set specific model
261-
field_name = field.name
262-
gfk_info = helpers.lookup_class_typeinfo(api, field.__class__)
263-
if gfk_info is None:
264-
gfk_set_type: MypyType = AnyType(TypeOfAny.unannotated)
265-
else:
266-
gfk_set_type = helpers.get_private_descriptor_type(
267-
gfk_info, "_pyi_private_set_type", is_nullable=True
268-
)
269-
expected_types[field_name] = gfk_set_type
257+
elif contenttypes_in_apps:
258+
from django.contrib.contenttypes.fields import GenericForeignKey
259+
260+
if isinstance(field, GenericForeignKey):
261+
# it's generic, so cannot set specific model
262+
field_name = field.name
263+
gfk_info = helpers.lookup_class_typeinfo(api, field.__class__)
264+
if gfk_info is None:
265+
gfk_set_type: MypyType = AnyType(TypeOfAny.unannotated)
266+
else:
267+
gfk_set_type = helpers.get_private_descriptor_type(
268+
gfk_info, "_pyi_private_set_type", is_nullable=True
269+
)
270+
expected_types[field_name] = gfk_set_type
270271

271272
return expected_types
272273

@@ -510,7 +511,7 @@ def resolve_lookup_expected_type(
510511
for lookup_base in helpers.iter_bases(lookup_info):
511512
if lookup_base.args and isinstance((lookup_type := get_proper_type(lookup_base.args[0])), Instance):
512513
# if it's Field, consider lookup_type a __get__ of current field
513-
if isinstance(lookup_type, Instance) and lookup_type.type.fullname == fullnames.FIELD_FULLNAME:
514+
if lookup_type.type.fullname == fullnames.FIELD_FULLNAME:
514515
field_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), field.__class__)
515516
if field_info is None:
516517
return AnyType(TypeOfAny.explicit)

mypy_django_plugin/transformers/models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ class AddPrimaryKeyAlias(AddDefaultPrimaryKey):
317317
def run_with_model_cls(self, model_cls: type[Model]) -> None:
318318
# We also need to override existing `pk` definition from `stubs`:
319319
auto_field = model_cls._meta.pk
320-
if auto_field:
320+
if auto_field is not None:
321321
self.create_autofield(
322322
auto_field=auto_field,
323323
dest_name="pk",
@@ -1147,13 +1147,13 @@ def get_annotated_type(
11471147
"""
11481148
if model_type.extra_attrs:
11491149
extra_attrs = ExtraAttrs(
1150-
attrs={**model_type.extra_attrs.attrs, **(fields_dict.items if fields_dict is not None else {})},
1150+
attrs={**model_type.extra_attrs.attrs, **fields_dict.items},
11511151
immutable=model_type.extra_attrs.immutable.copy(),
11521152
mod_name=None,
11531153
)
11541154
else:
11551155
extra_attrs = ExtraAttrs(
1156-
attrs=fields_dict.items if fields_dict is not None else {},
1156+
attrs=fields_dict.items,
11571157
immutable=None,
11581158
mod_name=None,
11591159
)

mypy_django_plugin/transformers/querysets.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,7 @@ def extract_proper_type_queryset_annotate(ctx: MethodContext, django_context: Dj
258258
"builtins.dict", [api.named_generic_type("builtins.str", []), AnyType(TypeOfAny.from_omitted_generics)]
259259
)
260260
elif isinstance(original_row_type, TupleType):
261-
fallback: Instance = original_row_type.partial_fallback
262-
if fallback is not None and fallback.type.has_base("typing.NamedTuple"):
261+
if original_row_type.partial_fallback.type.has_base("typing.NamedTuple"):
263262
# TODO: Use a NamedTuple which contains the known fields, but also
264263
# falls back to allowing any attribute access.
265264
row_type = AnyType(TypeOfAny.implementation_artifact)
@@ -535,10 +534,11 @@ def extract_prefetch_related_annotations(ctx: MethodContext, django_context: Dja
535534
536535
See https://docs.djangoproject.com/en/5.2/ref/models/querysets/#prefetch-objects
537536
"""
537+
api = helpers.get_typechecker_api(ctx)
538+
538539
if not (
539540
isinstance(ctx.type, Instance)
540541
and isinstance((default_return_type := get_proper_type(ctx.default_return_type)), Instance)
541-
and (api := helpers.get_typechecker_api(ctx))
542542
and (qs_model := helpers.get_model_info_from_qs_ctx(ctx, django_context)) is not None
543543
and ctx.args
544544
and ctx.arg_types

tests/assert_type/db/models/test_enums.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -270,24 +270,24 @@ class Joker(TextChoices):
270270
assert_type(Award.choices, list[tuple[str, str]]) # pyright: ignore[reportAssertTypeFailure]
271271

272272
# Assertions for mixing multiple choices types with consistent base types - only `IntegerChoices`.
273-
x = (Suit, Vehicle)
274-
assert_type([member.label for choices in x for member in choices], list[_StrOrPromise])
275-
assert_type([member.value for choices in x for member in choices], list[int])
273+
x0 = (Suit, Vehicle)
274+
assert_type([member.label for choices in x0 for member in choices], list[_StrOrPromise])
275+
assert_type([member.value for choices in x0 for member in choices], list[int])
276276

277277
# Assertions for mixing multiple choices types with consistent base types - only `TextChoices`.
278-
x = (Medal, Gender)
279-
assert_type([member.label for choices in x for member in choices], list[_StrOrPromise])
280-
assert_type([member.value for choices in x for member in choices], list[str])
278+
x1 = (Medal, Gender)
279+
assert_type([member.label for choices in x1 for member in choices], list[_StrOrPromise])
280+
assert_type([member.value for choices in x1 for member in choices], list[str])
281281

282282
# Assertions for mixing multiple choices types with different base types - `IntegerChoices` and `TextChoices`.
283-
x = (Medal, Suit)
284-
assert_type([member.label for choices in x for member in choices], list[_StrOrPromise])
285-
assert_type([member.value for choices in x for member in choices], list[int | str])
283+
x2 = (Medal, Suit)
284+
assert_type([member.label for choices in x2 for member in choices], list[_StrOrPromise])
285+
assert_type([member.value for choices in x2 for member in choices], list[int | str])
286286

287287
# Assertions for mixing multiple choices types with consistent base types - custom types.
288-
x = (Constants, Separator)
289-
assert_type([member.label for choices in x for member in choices], list[_StrOrPromise])
290-
assert_type([member.value for choices in x for member in choices], list[Any])
288+
x3 = (Constants, Separator)
289+
assert_type([member.label for choices in x3 for member in choices], list[_StrOrPromise])
290+
assert_type([member.value for choices in x3 for member in choices], list[Any])
291291

292292

293293
# Assertions for choices objects defined and aliased in a model.

tests/typecheck/conf/test_settings.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- path: myapp/lib.py
1919
content: |
2020
from typing import TYPE_CHECKING
21+
from typing_extensions import reveal_type
2122
import django.conf
2223
2324
settings = django.conf.settings

tests/typecheck/contrib/admin/test_decorators.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
- case: test_admin_decorators_display
22
main: |
3+
from typing_extensions import reveal_type
4+
35
from django.db import models
46
57
from django.contrib import admin
@@ -56,6 +58,8 @@
5658
5759
- case: test_admin_decorators_action
5860
main: |
61+
from typing_extensions import reveal_type
62+
5963
from django.db import models
6064
6165
from django.contrib import admin

tests/typecheck/contrib/auth/test_auth.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
- case: test_objects_using_auth_user_model_picks_up_configured_type
22
main: |
3+
from typing_extensions import reveal_type
34
from django.contrib.auth import authenticate, aauthenticate, get_user, get_user_model, login
45
from django.contrib.auth.backends import ModelBackend
56
from django.contrib.auth.decorators import user_passes_test
@@ -32,6 +33,7 @@
3233
3334
- case: test_objects_using_auth_user_model_uses_builtin_auth_user_per_default
3435
main: |
36+
from typing_extensions import reveal_type
3537
from django.contrib.auth import authenticate, aauthenticate, get_user, get_user_model, login
3638
from django.contrib.auth.backends import ModelBackend
3739
from django.contrib.auth.decorators import user_passes_test
@@ -62,6 +64,7 @@
6264
6365
- case: test_objects_for_auth_user_model_returns_stub_types_when_contrib_auth_is_not_installed
6466
main: |
67+
from typing_extensions import reveal_type
6568
from django.contrib.auth import authenticate, aauthenticate, get_user, get_user_model, login
6669
from django.contrib.auth.backends import ModelBackend
6770
from django.contrib.auth.base_user import AbstractBaseUser

tests/typecheck/contrib/auth/test_decorators.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
- case: login_required_bare
22
main: |
33
from typing import Any
4+
from typing_extensions import reveal_type
45
from django.contrib.auth.decorators import login_required
56
from django.http import HttpRequest, HttpResponse
67
@login_required
@@ -9,13 +10,15 @@
910
- case: login_required_bare_async
1011
main: |
1112
from typing import Any
13+
from typing_extensions import reveal_type
1214
from django.contrib.auth.decorators import login_required
1315
from django.http import HttpRequest, HttpResponse
1416
@login_required
1517
async def view_func(request: HttpRequest) -> HttpResponse: ...
1618
reveal_type(view_func) # N: Revealed type is "def (request: django.http.request.HttpRequest) -> typing.Coroutine[Any, Any, django.http.response.HttpResponse]"
1719
- case: login_required_fancy
1820
main: |
21+
from typing_extensions import reveal_type
1922
from django.contrib.auth.decorators import login_required
2023
from django.core.handlers.wsgi import WSGIRequest
2124
from django.http import HttpResponse
@@ -24,6 +27,7 @@
2427
reveal_type(view_func) # N: Revealed type is "def (request: django.core.handlers.wsgi.WSGIRequest, arg: builtins.str) -> django.http.response.HttpResponse"
2528
- case: login_required_fancy_async
2629
main: |
30+
from typing_extensions import reveal_type
2731
from django.contrib.auth.decorators import login_required
2832
from django.core.handlers.asgi import ASGIRequest
2933
from django.http import HttpResponse
@@ -32,6 +36,7 @@
3236
reveal_type(view_func) # N: Revealed type is "def (request: django.core.handlers.asgi.ASGIRequest, arg: builtins.str) -> typing.Coroutine[Any, Any, django.http.response.HttpResponse]"
3337
- case: login_required_weird
3438
main: |
39+
from typing_extensions import reveal_type
3540
from django.contrib.auth.decorators import login_required
3641
from django.http import HttpRequest, HttpResponse
3742
# This is non-conventional usage, but covered in Django tests, so we allow it.
@@ -46,13 +51,15 @@
4651
def view_func2(request: Any) -> str: ...
4752
- case: user_passes_test
4853
main: |
54+
from typing_extensions import reveal_type
4955
from django.contrib.auth.decorators import user_passes_test
5056
from django.http import HttpRequest, HttpResponse
5157
@user_passes_test(lambda u: u.get_username().startswith('super'))
5258
def view_func(request: HttpRequest) -> HttpResponse: ...
5359
reveal_type(view_func) # N: Revealed type is "def (request: django.http.request.HttpRequest) -> django.http.response.HttpResponse"
5460
- case: user_passes_test_async
5561
main: |
62+
from typing_extensions import reveal_type
5663
from django.contrib.auth.decorators import user_passes_test
5764
from django.http import HttpRequest, HttpResponse
5865
@user_passes_test(lambda u: u.get_username().startswith('super'))
@@ -66,13 +73,15 @@
6673
def view_func(request: HttpRequest) -> HttpResponse: ...
6774
- case: permission_required
6875
main: |
76+
from typing_extensions import reveal_type
6977
from django.contrib.auth.decorators import permission_required
7078
from django.http import HttpRequest, HttpResponse
7179
@permission_required('polls.can_vote')
7280
def view_func(request: HttpRequest) -> HttpResponse: ...
7381
reveal_type(view_func) # N: Revealed type is "def (request: django.http.request.HttpRequest) -> django.http.response.HttpResponse"
7482
- case: permission_required_async
7583
main: |
84+
from typing_extensions import reveal_type
7685
from django.contrib.auth.decorators import permission_required
7786
from django.http import HttpRequest, HttpResponse
7887
@permission_required('polls.can_vote')

tests/typecheck/contrib/contenttypes/test_models.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
- case: test_contenttypes_models
22
main: |
3+
from typing_extensions import reveal_type
34
from django.contrib.contenttypes.models import ContentType
45
56
c = ContentType.objects.create(app_label='abc', model='abc')

0 commit comments

Comments
 (0)