Skip to content

Commit cc73b3b

Browse files
authored
ruff: Removes all the ANN type ignores and fixes them (#1237)
1 parent 7e2542a commit cc73b3b

File tree

7 files changed

+113
-79
lines changed

7 files changed

+113
-79
lines changed

pyproject.toml

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -198,34 +198,28 @@ extend-select = [
198198
"RUF", # Ruff-specific rules
199199
]
200200
ignore = [
201-
"D100", # Missing docstring in public module
202-
"D101", # Missing docstring in public class
203-
"D102", # Missing docstring in public method
204-
"D103", # Missing docstring in public function
205-
"D104", # Missing docstring in public package
206-
"D105", # Missing docstring in magic method
207-
"D107", # Missing docstring in __init__
208-
"D200", # One-line docstring should fit on one line
209-
"D202", # No blank lines allowed after function docstring
210-
"D203", # Class definitions that are not preceded by a blank line
211-
"D205", # 1 blank line required between summary line and description
212-
"D209", # Multi-line docstring closing quotes should be on a separate line
213-
"D212", # Multi-line docstring summary should start at the first line
214-
"D213", # Multi-line docstring summary should start at the second line
215-
"D400", # First line should end with a period
216-
"D401", # First line of docstring should be in imperative mood
217-
"D404", # First word of the docstring should not be "This"
218-
"D415", # First line should end with a period, question mark, or exclamation point
219-
"S101", # Use of `assert` detected
201+
"ANN401", # Dynamically typed expressions (typing.Any) are disallowed
202+
"D100", # Missing docstring in public module
203+
"D101", # Missing docstring in public class
204+
"D102", # Missing docstring in public method
205+
"D103", # Missing docstring in public function
206+
"D104", # Missing docstring in public package
207+
"D105", # Missing docstring in magic method
208+
"D107", # Missing docstring in __init__
209+
"D200", # One-line docstring should fit on one line
210+
"D202", # No blank lines allowed after function docstring
211+
"D203", # Class definitions that are not preceded by a blank line
212+
"D205", # 1 blank line required between summary line and description
213+
"D209", # Multi-line docstring closing quotes should be on a separate line
214+
"D212", # Multi-line docstring summary should start at the first line
215+
"D213", # Multi-line docstring summary should start at the second line
216+
"D400", # First line should end with a period
217+
"D401", # First line of docstring should be in imperative mood
218+
"D404", # First word of the docstring should not be "This"
219+
"D415", # First line should end with a period, question mark, or exclamation point
220+
"S101", # Use of `assert` detected
220221

221222
# TODO - need to fix these
222-
"ANN001", # Missing type annotation for function argument
223-
"ANN002", # Missing type annotation for public function
224-
"ANN003", # Missing type annotation for public method
225-
"ANN201", # Missing return type annotation for public function
226-
"ANN202", # Missing return type annotation for private function
227-
"ANN204", # Missing return type annotation for special method
228-
"ANN401", # Dynamically typed expressions .. are disallowed
229223
"ARG001", # Unused function argument
230224
"ARG002", # Unused method argument
231225
"C901", # .. is too complex

pytest_django/asserts.py

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from __future__ import annotations
66

7-
from collections.abc import Sequence
87
from functools import wraps
98
from typing import TYPE_CHECKING, Any, Callable
109

@@ -26,11 +25,11 @@ class MessagesTestCase(MessagesTestMixin, TestCase):
2625
test_case = TestCase("run")
2726

2827

29-
def _wrapper(name: str):
28+
def _wrapper(name: str) -> Callable[..., Any]:
3029
func = getattr(test_case, name)
3130

3231
@wraps(func)
33-
def assertion_func(*args, **kwargs):
32+
def assertion_func(*args: Any, **kwargs: Any) -> Any:
3433
return func(*args, **kwargs)
3534

3635
return assertion_func
@@ -56,7 +55,12 @@ def assertion_func(*args, **kwargs):
5655

5756

5857
if TYPE_CHECKING:
58+
from collections.abc import Collection, Iterator, Sequence
59+
from contextlib import AbstractContextManager
60+
from typing import overload
61+
5962
from django import forms
63+
from django.db.models import Model, QuerySet, RawQuerySet
6064
from django.http.response import HttpResponseBase
6165

6266
def assertRedirects(
@@ -111,34 +115,34 @@ def assertTemplateUsed(
111115
template_name: str | None = ...,
112116
msg_prefix: str = ...,
113117
count: int | None = ...,
114-
): ...
118+
) -> None: ...
115119

116120
def assertTemplateNotUsed(
117121
response: HttpResponseBase | str | None = ...,
118122
template_name: str | None = ...,
119123
msg_prefix: str = ...,
120-
): ...
124+
) -> None: ...
121125

122126
def assertRaisesMessage(
123127
expected_exception: type[Exception],
124128
expected_message: str,
125-
*args,
126-
**kwargs,
127-
): ...
129+
*args: Any,
130+
**kwargs: Any,
131+
) -> None: ...
128132

129133
def assertWarnsMessage(
130134
expected_warning: Warning,
131135
expected_message: str,
132-
*args,
133-
**kwargs,
134-
): ...
136+
*args: Any,
137+
**kwargs: Any,
138+
) -> None: ...
135139

136140
def assertFieldOutput(
137-
fieldclass,
138-
valid,
139-
invalid,
140-
field_args=...,
141-
field_kwargs=...,
141+
fieldclass: type[forms.Field],
142+
valid: Any,
143+
invalid: Any,
144+
field_args: Any = ...,
145+
field_kwargs: Any = ...,
142146
empty_value: str = ...,
143147
) -> None: ...
144148

@@ -194,34 +198,44 @@ def assertXMLNotEqual(
194198

195199
# Removed in Django 5.1: use assertQuerySetEqual.
196200
def assertQuerysetEqual(
197-
qs,
198-
values,
199-
transform=...,
201+
qs: Iterator[Any] | list[Model] | QuerySet | RawQuerySet,
202+
values: Collection[Any],
203+
transform: Callable[[Model], Any] | type[str] | None = ...,
200204
ordered: bool = ...,
201205
msg: str | None = ...,
202206
) -> None: ...
203207

204208
def assertQuerySetEqual(
205-
qs,
206-
values,
207-
transform=...,
209+
qs: Iterator[Any] | list[Model] | QuerySet | RawQuerySet,
210+
values: Collection[Any],
211+
transform: Callable[[Model], Any] | type[str] | None = ...,
208212
ordered: bool = ...,
209213
msg: str | None = ...,
210214
) -> None: ...
211215

216+
@overload
217+
def assertNumQueries(
218+
num: int, func: None = None, *, using: str = ...
219+
) -> AbstractContextManager[None]: ...
220+
221+
@overload
222+
def assertNumQueries(
223+
num: int, func: Callable[..., Any], *args: Any, using: str = ..., **kwargs: Any
224+
) -> None: ...
225+
212226
def assertNumQueries(
213227
num: int,
214228
func=...,
215-
*args,
229+
*args: Any,
216230
using: str = ...,
217-
**kwargs,
231+
**kwargs: Any,
218232
): ...
219233

220234
# Added in Django 5.0.
221235
def assertMessages(
222236
response: HttpResponseBase,
223237
expected_messages: Sequence[Message],
224-
*args,
238+
*args: Any,
225239
ordered: bool = ...,
226240
) -> None: ...
227241

pytest_django/django_compat.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,23 @@
22
# this is the case before you call them.
33
from __future__ import annotations
44

5+
from typing import TYPE_CHECKING
6+
57
import pytest
68

79

10+
if TYPE_CHECKING:
11+
from typing import TypeAlias
12+
13+
from django.contrib.auth.models import AbstractBaseUser
14+
15+
_User: TypeAlias = AbstractBaseUser
16+
17+
_UserModel: TypeAlias = type[_User]
18+
19+
__all__ = ("_User", "_UserModel")
20+
21+
822
def is_django_unittest(request_or_item: pytest.FixtureRequest | pytest.Item) -> bool:
923
"""Returns whether the request or item is a Django test case."""
1024
from django.test import SimpleTestCase

pytest_django/fixtures.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from collections.abc import Generator, Iterable, Sequence
77
from contextlib import AbstractContextManager, contextmanager
88
from functools import partial
9-
from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Protocol, Union
9+
from typing import TYPE_CHECKING, Protocol
1010

1111
import pytest
1212

@@ -16,16 +16,18 @@
1616

1717

1818
if TYPE_CHECKING:
19+
from typing import Any, Callable, Literal, Optional, Union
20+
1921
import django
2022
import django.test
2123

2224
from . import DjangoDbBlocker
25+
from .django_compat import _User, _UserModel
2326

24-
25-
_DjangoDbDatabases = Optional[Union[Literal["__all__"], Iterable[str]]]
26-
_DjangoDbAvailableApps = Optional[list[str]]
27-
# transaction, reset_sequences, databases, serialized_rollback, available_apps
28-
_DjangoDb = tuple[bool, bool, _DjangoDbDatabases, bool, _DjangoDbAvailableApps]
27+
_DjangoDbDatabases = Optional[Union[Literal["__all__"], Iterable[str]]]
28+
_DjangoDbAvailableApps = Optional[list[str]]
29+
# transaction, reset_sequences, databases, serialized_rollback, available_apps
30+
_DjangoDb = tuple[bool, bool, _DjangoDbDatabases, bool, _DjangoDbAvailableApps]
2931

3032

3133
__all__ = [
@@ -337,7 +339,7 @@ def __getitem__(self, item: str) -> None:
337339
settings.MIGRATION_MODULES = DisableMigrations()
338340

339341
class MigrateSilentCommand(migrate.Command):
340-
def handle(self, *args, **kwargs):
342+
def handle(self, *args: Any, **kwargs: Any) -> Any:
341343
kwargs["verbosity"] = 0
342344
return super().handle(*args, **kwargs)
343345

@@ -456,15 +458,15 @@ def async_client() -> django.test.AsyncClient:
456458

457459

458460
@pytest.fixture
459-
def django_user_model(db: None):
461+
def django_user_model(db: None) -> _UserModel:
460462
"""The class of Django's user model."""
461463
from django.contrib.auth import get_user_model
462464

463-
return get_user_model()
465+
return get_user_model() # type: ignore[no-any-return]
464466

465467

466468
@pytest.fixture
467-
def django_username_field(django_user_model) -> str:
469+
def django_username_field(django_user_model: _UserModel) -> str:
468470
"""The fieldname for the username used with Django's user model."""
469471
field: str = django_user_model.USERNAME_FIELD
470472
return field
@@ -473,9 +475,9 @@ def django_username_field(django_user_model) -> str:
473475
@pytest.fixture
474476
def admin_user(
475477
db: None,
476-
django_user_model,
478+
django_user_model: _User,
477479
django_username_field: str,
478-
):
480+
) -> _User:
479481
"""A Django admin user.
480482
481483
This uses an existing user with username "admin", or creates a new one with
@@ -504,7 +506,7 @@ def admin_user(
504506
@pytest.fixture
505507
def admin_client(
506508
db: None,
507-
admin_user,
509+
admin_user: _User,
508510
) -> django.test.Client:
509511
"""A Django test client logged in as an admin user."""
510512
from django.test import Client
@@ -550,14 +552,14 @@ def __delattr__(self, attr: str) -> None:
550552

551553
self._to_restore.append(override)
552554

553-
def __setattr__(self, attr: str, value) -> None:
555+
def __setattr__(self, attr: str, value: Any) -> None:
554556
from django.test import override_settings
555557

556558
override = override_settings(**{attr: value})
557559
override.enable()
558560
self._to_restore.append(override)
559561

560-
def __getattr__(self, attr: str):
562+
def __getattr__(self, attr: str) -> Any:
561563
from django.conf import settings
562564

563565
return getattr(settings, attr)
@@ -570,7 +572,7 @@ def finalize(self) -> None:
570572

571573

572574
@pytest.fixture
573-
def settings():
575+
def settings() -> Generator[SettingsWrapper, None, None]:
574576
"""A Django settings object which restores changes after the testrun"""
575577
skip_if_no_django()
576578

@@ -580,7 +582,9 @@ def settings():
580582

581583

582584
@pytest.fixture(scope="session")
583-
def live_server(request: pytest.FixtureRequest):
585+
def live_server(
586+
request: pytest.FixtureRequest,
587+
) -> Generator[live_server_helper.LiveServer, None, None]:
584588
"""Run a live Django server in the background during tests
585589
586590
The address the server is started from is taken from the

pytest_django/live_server_helper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def url(self) -> str:
8484
def __str__(self) -> str:
8585
return self.url
8686

87-
def __add__(self, other) -> str:
87+
def __add__(self, other: str) -> str:
8888
return f"{self}{other}"
8989

9090
def __repr__(self) -> str:

pytest_django/plugin.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from collections.abc import Generator
1616
from contextlib import AbstractContextManager
1717
from functools import reduce
18-
from typing import TYPE_CHECKING, NoReturn
18+
from typing import TYPE_CHECKING
1919

2020
import pytest
2121

@@ -54,6 +54,8 @@
5454

5555

5656
if TYPE_CHECKING:
57+
from typing import Any, NoReturn
58+
5759
import django
5860

5961

@@ -186,7 +188,7 @@ def _handle_import_error(extra_message: str) -> Generator[None, None, None]:
186188
raise ImportError(msg) from None
187189

188190

189-
def _add_django_project_to_path(args) -> str:
191+
def _add_django_project_to_path(args: list[str]) -> str:
190192
def is_django_project(path: pathlib.Path) -> bool:
191193
try:
192194
return path.is_dir() and (path / "manage.py").exists()
@@ -198,7 +200,7 @@ def arg_to_path(arg: str) -> pathlib.Path:
198200
arg = arg.split("::", 1)[0]
199201
return pathlib.Path(arg)
200202

201-
def find_django_path(args) -> pathlib.Path | None:
203+
def find_django_path(args: list[str]) -> pathlib.Path | None:
202204
str_args = (str(arg) for arg in args)
203205
path_args = [arg_to_path(x) for x in str_args if not x.startswith("-")]
204206

@@ -571,7 +573,7 @@ def _django_setup_unittest(
571573

572574
original_runtest = TestCaseFunction.runtest
573575

574-
def non_debugging_runtest(self) -> None:
576+
def non_debugging_runtest(self) -> None: # noqa: ANN001
575577
self._testcase(result=self)
576578

577579
from django.test import SimpleTestCase
@@ -831,7 +833,7 @@ def _dj_db_wrapper(self) -> django.db.backends.base.base.BaseDatabaseWrapper:
831833
def _save_active_wrapper(self) -> None:
832834
self._history.append(self._dj_db_wrapper.ensure_connection)
833835

834-
def _blocking_wrapper(*args, **kwargs) -> NoReturn:
836+
def _blocking_wrapper(*args: Any, **kwargs: Any) -> NoReturn:
835837
__tracebackhide__ = True
836838
raise RuntimeError(
837839
"Database access not allowed, "

0 commit comments

Comments
 (0)